3

と の 2 つのテーブルがあるAとしBます。

テーブルAには複数レベルのインデックス(a, b)と 1 つの列 (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は、一意でないインデックス ( ) を持つ別の 1 列 (ts) テーブルaです。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指定されたタイムスタンプの後に、各イベント タイプが最初に発生したタイムスタンプから見つけたいと思います。つまり、 ts の代わりに table で指定された「ts の後に発生する最小値」を含む同じ形状のテーブルを取得したいと思います。AbAB

だから、私の目標は次のようになります。

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 のオーダーであると考えてくださいa。秒。

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

特に必要なデータが上記で説明したような C によって完全に記述されていることを考えると、RAM に保持するには少し多すぎるかもしれません。

どうすればパフォーマンスを改善できますか?

4

2 に答える 2

3

サンプルデータを提供していただきありがとうございます。予想される数億の配列サイズを考慮して、この回答を一般的な提案で更新しました。

  1. ラインプロファイル

    ラムダ関数の内臓をプロファイリングする行は、ほとんどの時間が B.ix[] (ここでは 1 回だけ呼び出されるようにリファクタリングされています) で費やされていることを示しています。

    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
    
  2. 上位レベルの構造よりも、組み込みのデータ型と生の 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
    
  3. 関数呼び出しをループに置き換える

    私が考えることができる最後の大きな改善点は、関数を 2 億回 (または A がいくら大きくても) 呼び出さないように、df.apply() を for ループに置き換えることです。

うまくいけば、これらのアイデアが役に立ちます。


オリジナルの表現力豊かなソリューションですが、メモリ効率は良くありません:

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
于 2012-12-17T22:29:51.467 に答える
2

numpy のブール配列表記法を使用する別のオプション。これは、元のものよりも桁違いに速いようです (この小さな例では、より大きなデータセットではさらに優れていると思われます...)
:ソートよりもはるかに高速なタスクです。

In [11]: AA.apply(lambda row: (B.ts.values[(B.ts.values >= row['ts']) &
                                           (B.index == row['a'])].min()),
                          axis=1)
Out[11]: 
0    4
1    7
2    5
3    7
4    7
5    8

In [12]: %timeit AA.apply(lambda row: (B.ts.values[(B.ts.values >= row['ts']) &(B.index == row['a'])].min()), axis=1)
1000 loops, best of 3: 1.46 ms per loop

これを列として に追加するだけなら、これが最速の方法のように思えますAA

あなたの例のように新しいデータフレームを作成していた場合-これを「かなり」テストしようとすると-遅くなります(ただし、元のデータフレームの2倍の速さです):

In [13]: %timeit 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'])
100 loops, best of 3: 10.3 ms per loop

In [14]: %timeit C = AA.apply(lambda row: (row[0], x[1], B.ts.values[(B.ts.values >= row['ts']) & (B.index == row['a'])].min()), axis=1)
100 loops, best of 3: 4.32 ms per loop
于 2012-12-17T22:38:11.493 に答える