我有一个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(以及相应的项目)的样本,并将几个方法应用于数据集。 以下是结果
请注意,我使用了pir程序的更新版本。 因此即使我们只考虑输出集的列表,胜利者也是非常的。
在我的整个数据集中,他的快速设置方法持续时间为5.05秒,而他基于列表的快速方法的持续时间仅为2.32秒。 从最初的115秒开始,这是一个巨大的收获! 再次感谢!
答案 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