【问题标题】:Filter dataframe by minimum number of values in groups按组中的最小值数过滤数据框
【发布时间】:2020-05-03 23:26:54
【问题描述】:

我有以下数据框结构:

#----------------------------------------------------------#
# Generate dataframe mock example.

# define categorical column.
grps = pd.DataFrame(['a', 'a', 'a', 'b', 'b', 'b']) 

# generate dataframe 1.
df1 = pd.DataFrame([[3, 4, 6, 8, 10, 4], 
                   [5, 7, 2, 8, 9, 6], 
                   [5, 3, 4, 8, 4, 6]]).transpose()

# introduce nan into dataframe 1.
for col in df1.columns:
    df1.loc[df1.sample(frac=0.1).index, col] = np.nan

# generate dataframe 2.
df2 = pd.DataFrame([[3, 4, 6, 8, 10, 4], 
                   [5, 7, 2, 8, 9, 6], 
                   [5, 3, 4, 8, 4, 6]]).transpose()

# concatenate categorical column and dataframes.
df = pd.concat([grps, df1, df2], axis = 1)

# Assign column headers.
df.columns = ['Groups', 1, 2, 3, 4, 5, 6]

# Set index as group column.
df = df.set_index('Groups')

# Generate stacked dataframe structure.
test_stack_df = df.stack(dropna = False).reset_index() 

# Change column names.
test_stack_df = test_stack_df.rename(columns = {'level_1': 'IDs',
                                                0: 'Values'})

#----------------------------------------------------------#

原始数据框 - 堆叠前的“df”:

Groups  1   2   3   4   5   6
a       3   5   5   3   5   5
a      nan nan  3   4   7   3
a       6   2  nan  6   2   4
b       8   8   8   8   8   8
b      10   9   4  10   9   4
b       4   6   6   4   6   6

我想过滤列,使每个组中至少有 3 个有效值 - 'a' 和 'b'。最终输出应该只有第 4、5、6 列。 我目前正在使用以下方法:

# Function to define boolean series.
def filter_vals(test_stack_df, orig_df):
    # Reset index.
    df_idx_reset = orig_df.reset_index()

    # Generate list with size of each 'Group'.
    grp_num = pd.value_counts(df_idx_reset['Groups']).to_list()

    # Data series for each 'Group'.
    expt_class_1 = test_stack_df.head(grp_num[0])
    expt_class_2 = test_stack_df.tail(grp_num[1])

    # Check if both 'Groups' contain at least 3 values per 'ID'.
    valid_IDs = len(expt_class_1['Values'].value_counts()) >=3 & \
                len(expt_class_2['Values'].value_counts()) >=3

    # Return 'true' or 'false'
    return(valid_IDs)

# Apply function to dataframe to generate boolean series.
bool_series = test_stack_df.groupby('IDs').apply(filter_vals, df)

# Transpose original dataframe.
df_T = df.transpose()

# Filter by boolean series & transpose again.
df_filtered = df_T[bool_series].transpose()

我可以通过应用pandas.dataframe.dropna() 方法并使用阈值 6 以最小的麻烦实现这一点。但是,这不会考虑不同大小的组或允许我指定当前代码所做的最小数量的值.

对于较大的数据帧,即 4000+ 列,代码有点慢,即需要大约 20 秒才能完成过滤过程。我已经尝试过使用groupby & transform 直接访问原始数据帧的替代方法,但无法正常工作。

有没有更简单快捷的方法?感谢您的宝贵时间!

编辑:2020 年 3 月 5 日 (15:58) - 刚刚在上面的函数中发现了一些不清楚的地方。仍然有效,但已澄清变量名称。很抱歉造成混乱!

【问题讨论】:

    标签: python-3.x pandas dataframe filtering pandas-groupby


    【解决方案1】:

    这将为您解决问题:

    df.notna().groupby(level='Groups').sum(axis=0).ge(3).all(axis=0)
    

    输出:

    1    False
    2    False
    3    False
    4     True
    5     True
    6     True
    dtype: bool
    

    【讨论】:

    • 嗨格热戈兹!感谢您的方法和尝试。我得到了这个'TypeError:_cython_agg_general()有一个意外的关键字参数'axis'。
    • 您好,尝试升级pandas:pip3 install --upgrade pandas
    • 嗨,Grzegorz,我已经更新到 pandas 1.03(之前使用的是 1.0.2),但我仍然遇到同样的错误。我使用 Anaconda 和 Spyder 作为 IDE,虽然我认为这不会有什么不同。有什么建议吗?
    • 嗯,有些东西必须不是完全最新的(虽然这可能是 anaconda 发行版的限制,所以不一定你可以修复它) - 怎么样:df.notna().groupby(level='Groups').transform(sum, axis=0).ge(3).all()?跨度>
    • 或者:df.notna().groupby(level='Groups').sum(axis=0).ge(3).all(),因为您安装了最新的pandas?
    猜你喜欢
    • 1970-01-01
    • 2019-04-02
    • 2018-10-09
    • 1970-01-01
    • 1970-01-01
    • 2017-12-13
    • 1970-01-01
    • 1970-01-01
    • 2015-02-16
    相关资源
    最近更新 更多