假设我有两个表 A
和 B
.
表格A
有一个多级索引(a, b)
和一列 (ts)。
b
明确确定 ts。
A = pd.DataFrame(
[('a', 'x', 4),
('a', 'y', 6),
('a', 'z', 5),
('b', 'x', 4),
('b', 'z', 5),
('c', 'y', 6)],
columns=['a', 'b', 'ts']).set_index(['a', 'b'])
AA = A.reset_index()
表格B
是另一个具有非唯一索引 ( a
) 的单列 (ts) 表。
ts 在每个组的“内部”排序,即 B.ix[x]
为每个 x 排序。
此外,B.ix[x]
中总是一个值大于或等于
A
中的值.
B = pd.DataFrame(
dict(a=list('aaaaabbcccccc'),
ts=[1, 2, 4, 5, 7, 7, 8, 1, 2, 4, 5, 8, 9])).set_index('a')
其中的语义是 B
包含对索引指示的类型事件发生的观察。
我想从B
中找到在 A
中指定的时间戳之后每种事件类型第一次出现的时间戳对于 b
的每个值.换句话说,我想得到一个形状与 A
相同的表格。 ,而不是 ts 包含表 B
指定的“ts 之后出现的最小值” .
所以,我的目标是:
C:
('a', 'x') 4
('a', 'y') 7
('a', 'z') 5
('b', 'x') 7
('b', 'z') 7
('c', 'y') 8
我有一些工作代码,但速度非常慢。
C = AA.apply(lambda row: (
row[0],
row[1],
B.ix[row[0]].irow(np.searchsorted(B.ts[row[0]], row[2]))), axis=1).set_index(['a', 'b'])
分析显示罪魁祸首显然是 B.ix[row[0]].irow(np.searchsorted(B.ts[row[0]], row[2])))
.但是,从长远来看,使用合并/合并的标准解决方案会占用过多的 RAM。
考虑现在我有 1000 a
的,假设每个 a 的 b 的平均数量不变(可能是 100-200),并考虑每个 a 的观察数量可能是 300 的数量级。在生产中我将有 1000 个以上 a
的。
1,000,000 x 200 x 300 = 60,000,000,000
行数
保存在 RAM 中可能有点太多了,特别是考虑到我需要的数据由 C 语言完美描述,就像我上面讨论的那样。
我将如何提高性能?
最佳答案
感谢您提供示例数据。我已经用一般更新了这个答案 给出预期数组大小的建议,数以百万计。
线路简介
对您的 lambda 函数进行分析表明大部分时间都花在了 在 B.ix[] 中(已在此处重构为仅调用一次)。
In [91]: lprun -f stack.foo1 AA.apply(stack.foo1, B=B, axis=1) Timer unit: 1e-06 s File: stack.py Function: foo1 at line 4 Total time: 0.006651 s Line # Hits Time Per Hit % Time Line Contents ============================================================== 4 def foo1(row, B): 5 6 6158 1026.3 92.6 subset = B.ix[row[0]].ts 6 6 418 69.7 6.3 idx = np.searchsorted(subset, row[2]) 7 6 56 9.3 0.8 val = subset.irow(idx) 8 6 19 3.2 0.3 return val
考虑内置数据类型和原始 numpy 数组而不是更高级别的构造。
因为 B 在这里表现得像一个字典,并且多次访问同一个键,所以让我们将 df.ix 与普通的 Python 进行比较 字典(在别处预先计算)。具有 1M 键(唯一 A 值)的字典应该只需要 ~34MB(33% 容量:3 * 1e6 * 12 字节)。
In [102]: timeit B.ix['a'] 10000 loops, best of 3: 122 us per loop In [103]: timeit dct['a'] 10000000 loops, best of 3: 53.2 ns per loop
用循环替换函数调用
我能想到的最后一个主要改进是将 df.apply() 替换为 for 循环,以避免调用任何函数 200M 次(或者无论 A 有多大)。
希望这些想法有所帮助。
原始的、富有表现力的解决方案,但内存效率不高:
In [5]: CC = AA.merge(B, left_on='a', right_index=True)
In [6]: CC[CC.ts_x <= CC.ts_y].groupby(['a', 'b']).first()
Out[6]:
ts_x ts_y
a b
a x 4 4
y 6 7
z 5 5
b x 4 7
z 5 7
c y 6 8
关于python - 两个表之间的非标准交互以避免非常大的合并,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13918355/