numpy 或 pandas groupby 方式替换 2 个 for 循环

Posted

技术标签:

【中文标题】numpy 或 pandas groupby 方式替换 2 个 for 循环【英文标题】:numpy or pandas groupby way to replace 2 for loops 【发布时间】:2020-11-25 10:24:33 【问题描述】:

我有以下问题,主要类列表:

list_main_classes = [3,4]
data = pd.DataFrame(
    'label_col':[1,1,2,2,3,3,3,4,4], 
    'second_classes_column':[
        "class1", 
        "class2", 
        "class1", 
        "class2", 
        "class3", 
        "class3", 
        "class3", 
        "class4", 
        "class2"
    ])

有一个列"second_classes_column" 我基本上想做的是从列表"list_main_classes" 中删除一些满足某些条件的元素。什么条件?

    二等列不能命名"certain_name" "second_classes_column" 元素不得出现在'label_col' 创建的组之外。这意味着对于由“label_col”的元素 4 创建的组 "second_classes_column" 中不得有出现在其他组中的元素。在我们的例子中,元素 "class2" 不满足这一点,因为它已经出现在之前(第 2 行和第 4 行)。因此,我们将删除 4,但从 list_main_classes 中保留 3,因为它满足所有要求,

问题有没有更快的方法来做到这一点,Pandas groupby,numpy,已经完成了 2 个 for 循环?

【问题讨论】:

我发现很难遵循您的最终结果。你能提供一个简单的DataFrame 和一些示例数据吗?我最初的想法是创建一些布尔掩码,但我不确定这是否可行,因为我不知道您要过滤什么 好的,如果我能详细说明,请告诉我。 【参考方案1】:

您应该对数据执行合并,然后对生成的数据框进行过滤。

此外,如果"second_classes_column" 分配了多个唯一的"label_col",则它是无效的,因此您可以预先计算与每个"second_classes_column" 关联的label_cols 的数量。

# setup some useful variables
main_classes = pd.DataFrame("main_classes": list_main_classes)
count_unique_classes = data.groupby("second_classes_column")["label_col"].nunique().to_dict()

def your_logic(x):
    second_id = x["second_classes_column"]
    label_col = x["label_col"]
    
    case1 = second_id != "certain_class"
    case2 = count_unique_classes[second_id] > 1
    
    return case1 and case2

# merge the two data frames
joint_df = pd.merge(data, main_classes, left_on="label_col", right_on="main_classes")

# now you can easily do the filter and perform your logic
to_drop = joint_df.apply(your_logic, axis=1)
list_main_indexes_to_drop = joint_df[to_drop].main_classes

所以结果:

>>> list_main_indexes_to_drop.values
... array([4])

您的最终列表可以使用filter、集合操作或np.setdiff1d获得

>>> list(set(list_main_classes) - set(list_main_indexes_to_drop))
... [3]

>>> np.setdiff1d(list_main_classes, list_main_indexes_to_drop)
... array([3])

更新。您可能不喜欢 your_logicapply,因此您可以使用矢量化布尔运算来实现,如下所示:

# setup some useful variables
main_classes = pd.DataFrame("main_classes": list_main_classes)
count_unique_classes = data.groupby("second_classes_column")["label_col"].nunique().ge(2)
invalid_classes = set(count_unique_classes[count_unique_classes].index)

# merge the two data frames
joint_df = pd.merge(data, main_classes, left_on="label_col", right_on="main_classes")

# your logic
joint_df = joint_df[
    (joint_df.second_classes_column != "certain_class") & 
    (joint_df.second_classes_column.isin(invalid_classes)) 
]

# now you can easily do the filter and perform your logic
list_main_indexes_to_drop = joint_df.main_classes
list_main_indexes_to_drop.values

【讨论】:

以上是关于numpy 或 pandas groupby 方式替换 2 个 for 循环的主要内容,如果未能解决你的问题,请参考以下文章

Python pandas用法

python pandas groupby分组后的数据怎么用

pandas聚合和分组运算之groupby

在 pandas 和 numpy 中聚合 lambda 函数

使用 groupby 的 Pandas 占总数的百分比

Python:numpy/pandas 根据条件更改值