def filter_probes_by_nan_and_sd(data_df, probe_frac_cutoff, probe_sd_cutoff):
    """ Filter out probes whose fraction of samples measured is less than probe_frac_cutoff.
    Also remove probes with standard deviation higher than probe_sd_cutoff.

    Args:
        data_df (pandas df)
        probe_frac_cutoff (float b/w 0 and 1)
        probe_sd_cutoff (float)
    Returns:
        out_df (pandas df): potentially smaller than original df
    """
    # Number of NaNs per probe
    num_nans = data_df.isnull().sum(axis=1)

    # Number of samples
    num_samples = data_df.shape[1]

    # Fraction non-NaN per probe
    frac_non_nans_per_probe = 1 - num_nans/num_samples

    # Probe standard deviations
    probe_sds = data_df.std(axis=1)

    # Only return probes with more non-NaN data than probe_frac_cutoff
    # and lower sd than the cutoff
    probes_to_keep = ((frac_non_nans_per_probe > probe_frac_cutoff) &
                      (probe_sds < probe_sd_cutoff))
    out_df = data_df.loc[probes_to_keep, :]
    assert not out_df.empty, (
        "All probes were filtered out. Try reducing the NaN threshold and/or SD cutoff.")
    return out_df