たとえば、dfを作成します。
import pandas as pd
import random as randy
import numpy as np
df_size = int(1e6)
df = pd.DataFrame({'first': randy.sample(np.repeat([np.NaN,'Cat','Dog','Bear','Fish'],df_size),df_size),
'second': randy.sample(np.repeat([np.NaN,np.NaN,'Cat','Dog'],df_size),df_size),
'value': range(df_size)},
index=randy.sample(pd.date_range('2013-02-01 09:00:00.000000',periods=1e6,freq='U'),df_size)).sort_index()
そしてそれはこのように見えます:
first second value
2013-02-01 09:00:00 Fish Cat 95409
2013-02-01 09:00:00.000001 Dog Dog 323089
2013-02-01 09:00:00.000002 Fish Cat 785925
2013-02-01 09:00:00.000003 Dog Cat 866171
2013-02-01 09:00:00.000004 nan nan 665702
2013-02-01 09:00:00.000005 Cat nan 104257
2013-02-01 09:00:00.000006 nan nan 152926
2013-02-01 09:00:00.000007 Bear Cat 707747
私が欲しいのは、「2番目の」列の各値に対して、最初の「最後の」値が欲しいということです。
first second value new_value
2013-02-01 09:00:00 Fish Cat 95409 NaN
2013-02-01 09:00:00.000001 Dog Dog 323089 323089
2013-02-01 09:00:00.000002 Fish Cat 785925 NaN
2013-02-01 09:00:00.000003 Dog Cat 866171 NaN
2013-02-01 09:00:00.000004 nan nan 665702 NaN
2013-02-01 09:00:00.000005 Cat nan 104257 NaN
2013-02-01 09:00:00.000006 nan nan 152926 NaN
2013-02-01 09:00:00.000007 Bear Cat 707747 104257
おそらく、それは絶対的な最良の例ではありませんが、一番下の「second」が「Cat」の場合、「first」が「Cat」のときの最新の値が必要です。
実際のデータセットには1000以上のカテゴリがあるため、シンボルをループしてasof()を実行すると非常にコストがかかるように見えます。Cythonで文字列を渡すことはできませんでしたが、シンボルをintにマッピングし、ブルートフォースループを実行するとうまくいくと思います。もっとPython的なものを望んでいました。(それでもかなり速いです)
参照、およびやや壊れやすいCythonハックは次のようになります。
%%cython
import numpy as np
import sys
cimport cython
cimport numpy as np
ctypedef np.double_t DTYPE_t
def last_of(np.ndarray[DTYPE_t, ndim=1] some_values,np.ndarray[long, ndim=1] first_sym,np.ndarray[long, ndim=1] second_sym):
cdef long val_len = some_values.shape[0], sym1_len = first_sym.shape[0], sym2_len = second_sym.shape[0], i = 0
assert(sym1_len==sym2_len)
assert(val_len==sym1_len)
cdef int enum_space_size = max(first_sym)+1
cdef np.ndarray[DTYPE_t, ndim=1] last_values = np.zeros(enum_space_size, dtype=np.double) * np.NaN
cdef np.ndarray[DTYPE_t, ndim=1] res = np.zeros(val_len, dtype=np.double) * np.NaN
for i in range(0,val_len):
if first_sym[i]>=0:
last_values[first_sym[i]] = some_values[i]
if second_sym[i]<0 or second_sym[i]>=enum_space_size:
res[i] = np.NaN
else:
res[i] = last_values[second_sym[i]]
return res
そして、いくつかのdictがナンセンスを置き換えます:
syms= unique(df['first'].values)
enum_dict = dict(zip(syms,range(0,len(syms))))
enum_dict['nan'] = -1
df['enum_first'] = df['first'].replace(enum_dict)
df['enum_second'] = df['second'].replace(enum_dict)
df['last_value'] = last_of(df.value.values*1.0,df.enum_first.values.astype(int64),df.enum_second.values.astype(int64))
これには、「2番目の」列に最初の列にない値がある場合に問題が発生するという問題があります。(これを修正するための迅速な方法はわかりません...たとえば、2番目に「ロバ」を追加した場合)
1000万行あたりのcythonicの愚かなバージョンは、混乱全体で約21秒ですが、cython部分では約2秒です。(これはまともな量をより速くすることができます)
@HYRY-これはかなり堅固な解決策だと思います。1000万行のDFの場合、私のラップトップでは、これには約30秒かかります。
2番目のリストに最初のリストにないエントリが含まれている場合の簡単な処理方法がわからないことを考えると、かなり高価なisin以外に、HYRYのPythonバージョンはかなり良いと思います。