def preprocess_data(df, missing_value_threshold=1/3,):
    """
    Prof. George's preprocessing algorithm.
    
    parameters
    -------------   
    df : data frame
        The clean data frame results from the clean_raw_data() method.
    missing_value_threshold : float, default 1/3 
        The fraction of the missing values allowed in the time series 
        in defining the sparse genes.
    
    Returns
    -------------
    df : data frame
        Clean data frame
    """
    # print('\nStart preprocessing ....')
    # Step 1. set 0s to be NaN:
    df.mask( df==0.0, inplace=True )

    # Step 2. set values <1 to be 1
    df.mask( df<1, other=1, inplace=True )

    # # Check the number of constant genes with all-1s resulted from step 2
    # num_of_constant_genes = (df.var(axis=1) == 0).sum()
    # print('# of constant genes (all-1 genes) = ', num_of_constant_genes)

    # Step 3. remove the constant genes again
    df = df.loc[ df.var(axis=1) != 0 ]
    # num_of_constant_genes = (df.var(axis=1) == 0).sum()
    # print('No more constant genes? ', num_of_constant_genes == 0)

    # Step 4. Filter sparse genes
    number_of_non_NaNs_threshold = \
    df.shape[1] * (1 - missing_value_threshold)

    df.dropna( thresh=number_of_non_NaNs_threshold, inplace=True )
    # print('Shape of the dense dataframe : ', df.shape)
    # print('Finished preprocessing ----')

    return df