编程语言
首页 > 编程语言> > python – 两个表之间的非标准交互,以避免非常大的合并

python – 两个表之间的非标准交互,以避免非常大的合并

作者:互联网

假设我有两张桌子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在每个组的“内部”排序,即,对每个x排序B.ix [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,假设每个b的平均b数(可能是100-200),并且认为每个a的观测数量可能大约为300.在生产中我将有1000个a.

1,000,000 x 200 x 300 = 60,000,000,000行

保留在RAM中可能有点太多,特别是考虑到我需要的数据完全由C语言描述,就像我上面讨论的那样.

我将如何改善表现?

解决方法:

感谢您提供样本数据.我已经更新了这个答案
建议给出100万分之一的预期阵列大小.

>线轮廓

线性分析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在这里表现得像dict并且多次访问相同的密钥,让我们将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

>用循环替换函数调用

我能想到的最后一个主要改进是用for循环替换df.apply()以避免调用任何函数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,pandas,join,binary-search,merge
来源: https://codeday.me/bug/20190901/1783305.html