同时根据熊猫行的不同值进行过滤

时间:2018-06-27 11:30:54

标签: python-3.x performance pandas pandas-groupby

我有一个巨大的数据框,其中包含product_id及其property_id。请注意,对于每个属性,都以新索引开头。我需要同时针对每个product_id按不同的property_id值进行过滤。有什么办法可以快速做到吗?

out_df

product_id  property_id
0   3588    1
1   3588    2
2   3588    5
3   3589    1
4   3589    3
5   3589    5
6   3590    1
7   3590    2
8   3590    5

例如,想要通过每个属性(例如out_df.loc[(out_df['property_id'] == 1) & (out_df['property_id'] == 2)]分配给不同行的两个属性来过滤每个product_id的方法)。 我需要类似的东西,但同时要为每个product_id列的所有行工作。

我知道可以通过groupby进入列表

3587    [2, 1, 5]
3588    [1, 3, 5]
3590    [1, 2, 5]

并在列表中查找交集。

gp_df.apply(lambda r: {1, 2} < (set(r['property_id'])), axis=1)  

但是这需要花费时间,同时Pandas的通用过滤已为速度进行了极大的优化(请相信在ElasticSearch,Sphinx等搜索引擎内部使用一些棘手的向右和反向索引)。

预期的输出:{1和2}都在其中。

3587    [2, 1, 5]
3590    [1, 2, 5]

3 个答案:

答案 0 :(得分:1)

你的意思是这样吗?

Private Sub find_occurance(ByVal Country As String)
    Dim next_free_row As Long

    For Each Cell In Sheets("Sheet1").ListObjects("Table1").ListColumns(2).DataBodyRange
        If Trim(LCase(Cell.Value2)) = Trim(LCase(Country)) Then
            next_free_row = Sheets("Sheet2").Cells(Rows.Count, 1).End(xlUp).Row + 1
            Sheets("Sheet2").Cells(next_free_row, "A") = Cell.Offset(0, -1).Value2
            Sheets("Sheet2").Cells(next_free_row, "B") = Cell.Value2
        End If
    Next Cell
End Sub

如果需要,您可以根据product_id删除重复项...

答案 1 :(得分:1)

最简单的方法是将GroupBy.transform与比较集一起使用:

s = {1, 2}
a = df[df.groupby('product_id')['property_id'].transform(lambda r: s < set(r))]
print (a)
   product_id  property_id
0        3588            1
1        3588            2
2        3588            5
6        3590            1
7        3590            2
8        3590            5

另一种解决方案是仅过滤集合的值,首先删除重复项:

df1 = df[df['property_id'].isin(s) & ~df.duplicated(['product_id', 'property_id'])]

然后需要检查每个组的长度是否与this solution设置的长度相同:

f, u = df1['product_id'].factorize()
ids = df1.loc[np.bincount(f)[f] == len(s), 'product_id'].unique()

根据条件最后过滤带有product_id的所有行

a = df[df['product_id'].isin(ids)]
print (a)
   product_id  property_id
0        3588            1
1        3588            2
2        3588            5
6        3590            1
7        3590            2
8        3590            5

答案 2 :(得分:1)

由于这与功能性问题一样具有性能,因此我将采用这样的交叉方法:

df = pd.DataFrame({'product_id': [3588, 3588, 3588, 3589, 3589, 3589, 3590, 3590,3590], 
                   'property_id': [1, 2, 5, 1, 3, 5, 1, 2, 5]})

df = df.set_index(['property_id'])

print("The full DataFrame:")
print(df)

start = time()

for i in range(1000):
    s1 = df.loc[(1), 'product_id']
    s2 = df.loc[(2), 'product_id']

    s_done = pd.Series(list(set(s1).intersection(set(s2))))

print("Overlapping product_id's")
print(time()-start)

在ThinkPad T450上迭代查找1000次需要 0.93秒。我冒昧地测试了@jezrael的两个建议,它们分别在2.11和2.00秒时出现,groupby的方法在软件工程上是明智的,但是更优雅。

根据数据集的大小和性能的重要性,您还可以切换到更简单的数据类型,例如经典字典,以提高速度。


Jupyter笔记本可以在这里找到:pandas_fast_lookup_using_intersection.ipynb