DataFrame
の各行について同じdtype
の列にわたって異なる値を数える最も速い方法(純粋な平滑度の範囲内で)は何ですか?DataFrameの列間で効率的な行数を行単位でグループ化
詳細:私は次のようによって生成されたものに類似した(列の)日によって(行の)被験者によるカテゴリの成果のDataFrame
を、持っています。データセットは、店舗への各訪問で注文各顧客を飲む教えてくれる場合
import numpy as np
import pandas as pd
def genSampleData(custCount, dayCount, discreteChoices):
"""generate example dataset"""
np.random.seed(123)
return pd.concat([
pd.DataFrame({'custId':np.array(range(1,int(custCount)+1))}),
pd.DataFrame(
columns = np.array(['day%d' % x for x in range(1,int(dayCount)+1)]),
data = np.random.choice(a=np.array(discreteChoices),
size=(int(custCount), int(dayCount)))
)], axis=1)
は例えば、私は、顧客ごとに個別のドリンクの数を知っていただきたいと思います。 (例えば、下記testDf
)このユースケースでデータセットは、日よりも多くの科目を持つことになりますので、私は最も効率的な行方向の操作を見つけることを試みています:
# notional discrete choice outcome
drinkOptions, drinkIndex = np.unique(['coffee','tea','juice','soda','water'],
return_inverse=True)
# integer-coded discrete choice outcomes
d = genSampleData(2,3, drinkIndex)
d
# custId day1 day2 day3
#0 1 1 4 1
#1 2 3 2 1
# Count distinct choices per subject -- this is what I want to do efficiently on larger DF
d.iloc[:,1:].apply(lambda x: len(np.unique(x)), axis=1)
#0 2
#1 3
# Note: I have coded the choices as `int` rather than `str` to speed up comparisons.
# To reconstruct the choice names, we could do:
# d.iloc[:,1:] = drinkOptions[d.iloc[:,1:]]
私の元の試みを改善する
testDf = genSampleData(100000,3, drinkIndex) #---- Original attempts ---- %timeit -n20 testDf.iloc[:,1:].apply(lambda x: x.nunique(), axis=1) # I didn't wait for this to finish -- something more than 5 seconds per loop %timeit -n20 testDf.iloc[:,1:].apply(lambda x: len(x.unique()), axis=1) # Also too slow %timeit -n20 testDf.iloc[:,1:].apply(lambda x: len(np.unique(x)), axis=1) #20 loops, best of 3: 2.07 s per loop
、我々はpandas.DataFrame.apply()引数を受け入れることに注意してください:
raw=True
た場合のp assed関数は代わりにndarrayオブジェクトを受け取ります。 あなただけの、これは はるかに優れたパフォーマンスを実現しますnumpyの低減機能を適用する場合
これは半分以上でランタイムをカットしました:
%timeit -n20 testDf.iloc[:,1:].apply(lambda x: len(np.unique(x)), axis=1, raw=True)
#20 loops, best of 3: 721 ms per loop *best so far*
私は、これは純粋なnumpyのソリューションことに驚きましたraw=True
と上記に相当すると思われる、実際には少し遅かった:
%timeit -n20 np.apply_along_axis(lambda x: len(np.unique(x)), axis=1, arr = testDf.iloc[:,1:].values)
#20 loops, best of 3: 1.04 s per loop
最後に、私も試してみましたtranspo私がより効率的かもしれないと思ったcolumn-wise count distinctを行うためにデータを歌いなさい(少なくともDataFrame.apply()
のために、しかし意味のある差があるように思わなかった。
%timeit -n20 testDf.iloc[:,1:].T.apply(lambda x: len(np.unique(x)), raw=True)
#20 loops, best of 3: 712 ms per loop *best so far*
%timeit -n20 np.apply_along_axis(lambda x: len(np.unique(x)), axis=0, arr = testDf.iloc[:,1:].values.T)
# 20 loops, best of 3: 1.13 s per loop
これまでのところ、私の最善の解決策は、len(np.unique())
のdf.apply
の奇妙なミックスですが、私は他に何を試してみてください? DataFrame.groupby
とgroupby.SeriesGroupBy.nunique
と
は日カウント代表ですか?パフォーマンスの差に大きく影響するようです。 – ayhan
@面白い...面白い...私の特定のユースケースは日数が代表ですが、他のユーザーには注目に値する幅広いデータセットでは何か他のものがうまく動作すれば – C8H10N4O2
実際は反対です。少数の列がある場合、各列を他の列と比較する方がはるかに高速です。私は答えとして結果を掲示した。 – ayhan