从事务数据到集合列表的有效方式

时间:2016-12-02 08:01:26

标签: python performance list pandas set

我有一个csv文件,其中包含以下格式的交易数据

import pandas as pd
df = pd.DataFrame({'OrderID':[1,1,1,1,2,2], 'ItemID':[1,2,3,4,1,2]})
print(df)
   ItemID  OrderID
0       1        1
1       2        1
2       3        1
3       4        1
4       1        2
5       2        2

我想获得一个列表,其中包含每个OrderID的项目集。

这可以通过

获得
df.groupby('OrderID').apply(lambda x: set(x['ItemID'])).tolist()
[{1, 2, 3, 4}, {1, 2}]

但是,在包含900万行的csv文件中,这需要一些时间。因此,我想知道是否有更快的方法? 我对使用pandas的任何解决方案感兴趣,或者直接在.csv文件上运行

首先,我要感谢你们,感谢您的精彩投入! 我从我的真实数据中抽取了50000个OrderIds(以及相应的项目)的样本,并将几个方法应用于数据集。 以下是结果

BenchmarkResults

请注意,我使用了pir程序的更新版本。 因此即使我们只考虑输出集的列表,胜利者也是非常的。

在我的整个数据集中,他的快速设置方法持续时间为5.05秒,而他基于列表的快速方法的持续时间仅为2.32秒。 从最初的115秒开始,这是一个巨大的收获! 再次感谢!

2 个答案:

答案 0 :(得分:3)

您可以尝试SeriesGroupBy.unique,然后转换为numpy array,然后转到set list comprehension

arr = df.groupby('OrderID')['ItemID'].unique().values
print (arr)
[array([1, 2, 3, 4], dtype=int64) array([1, 2], dtype=int64)]

print ([set(v) for v in arr])
[{1, 2, 3, 4}, {1, 2}]

编辑更快在apply中使用unique

print (df.groupby('OrderID').apply(lambda x: set(x['ItemID'].unique())).tolist())

<强>计时

np.random.seed(123)
N = 1000000
df = pd.DataFrame(np.random.randint(30,size=(N,2)))
df.columns = ['OrderID','ItemID']

def pir(df):
    uo, io = np.unique(df.OrderID.values, return_inverse=True)
    ui, ii = np.unique(df.ItemID.values, return_inverse=True)
    def gu(i):
        return set(ui[ii[io == i]].tolist())
    return [gu(i) for i in range(len(uo))]

def divakar(df):
    a = df.values
    sidx = a[:,1].argsort(kind='mergesort')
    cut_idx = np.nonzero(a[sidx[1:],1] > a[sidx[:-1],1])[0]+1
    out = np.split(a[sidx,0], cut_idx)
    return list(map(set,out))
In [120]: %timeit (df.groupby('OrderID')
                     .apply(lambda x: set(x['ItemID'].unique())).tolist())
10 loops, best of 3: 92.7 ms per loop


In [121]: %timeit (df.groupby('OrderID').apply(lambda x: set(x['ItemID'])).tolist())
10 loops, best of 3: 168 ms per loop

In [122]: %timeit ([set(v) for v in df.groupby('OrderID')['ItemID'].unique().values])
10 loops, best of 3: 125 ms per loop

In [123]: %timeit (list(map(set,df.groupby('OrderID')['ItemID'].unique().values)))
10 loops, best of 3: 125 ms per loop

In [124]: %timeit (pir(df))
1 loop, best of 3: 276 ms per loop

In [125]: %timeit (divakar(df))
1 loop, best of 3: 190 ms per loop

答案 1 :(得分:3)

方法#1:使用数组拆分和set -

def divakar_v1(df):
    a = df.values
    sidx = a[:,1].argsort() # Use .argsort(kind='mergesort') to keep order
    cut_idx = np.nonzero(a[sidx[1:],1] > a[sidx[:-1],1])[0]+1
    out = np.split(a[sidx,0], cut_idx)
    return list(map(set,out))

方法#2:使用迭代数组切片和set -

def divakar_v2(df):
    data = df.values
    a = data[data[:,1].argsort()] # Use .argsort(kind='mergesort') to keep order
    stop = np.append(np.nonzero(a[1:,1] > a[:-1,1])[0]+1,a.size)
    start = np.append(0, stop[:-1])
    out_set = [set(a[start[i]:stop[i],0]) for i in range(len(start))]
    return out_set

根据'OrderID',我们在'ItemID'中会有唯一/不同的元素,我们还有两种方法可以跳过set()的使用,从而为我们提供了一个列表列表输出。这些都列在下面。

方法#3:使用数组拆分和list of lists as o/p -

def divakar_v3(df):
    a = df.values
    sidx = a[:,1].argsort() # Use .argsort(kind='mergesort') to keep order
    cut_idx = np.nonzero(a[sidx[1:],1] > a[sidx[:-1],1])[0]+1
    out = np.split(a[sidx,0], cut_idx)
    return list(map(list,out))

方法#4:使用迭代数组切片和list of lists as o/p -

def divakar_v4(df):
    data = df.values
    a = data[data[:,1].argsort()] # Use .argsort(kind='mergesort') to keep order
    stop = np.append(np.nonzero(a[1:,1] > a[:-1,1])[0]+1,a.size)
    start = np.append(0, stop[:-1])
    a0 = a[:,0].tolist()
    return [a0[start[i]:stop[i]] for i in range(len(start))]

运行时测试 -

In [145]: np.random.seed(123)
     ...: N = 100000
     ...: df = pd.DataFrame(np.random.randint(30,size=(N,2)))
     ...: df.columns = ['ItemID','OrderID']
     ...: 

In [146]: %timeit divakar_v1(df)
     ...: %timeit divakar_v2(df)
     ...: %timeit divakar_v3(df)
     ...: %timeit divakar_v4(df)
     ...: 
10 loops, best of 3: 21.1 ms per loop
10 loops, best of 3: 21.7 ms per loop
100 loops, best of 3: 16.7 ms per loop
100 loops, best of 3: 12.3 ms per loop