名前(person_name)、曜日、色(shirt_color)を列とするデータセットがあります。
一人一人が特定の日に特定の色のシャツを着ています。日数は任意です。
例えば。入力:
name day color
----------------
John 1 White
John 2 White
John 3 Blue
John 4 Blue
John 5 White
Tom 2 White
Tom 3 Blue
Tom 4 Blue
Tom 5 Black
Jerry 1 Black
Jerry 2 Black
Jerry 4 Black
Jerry 5 White
各人が最もよく使用する色を見つける必要があります。
例えば。結果:
name color
-------------
Jerry Black
John White
Tom Blue
結果を取得するために次の操作を実行しています。これは正常に動作しますが、非常に低速です。
most_frquent_list = [[name, group.color.mode()[0]]
for name, group in data.groupby('name')]
most_frquent_df = pd.DataFrame(most_frquent_list, columns=['name', 'color'])
ここで、500万の一意の名前を持つデータセットがあるとします。上記の操作を実行するための最良/最速の方法は何ですか?
numpy.add.at
_および_pandas.factorize
_これは高速であることを目的としています。ただし、読みやすいように整理しようとしました。
_i, r = pd.factorize(df.name)
j, c = pd.factorize(df.color)
n, m = len(r), len(c)
b = np.zeros((n, m), dtype=np.int64)
np.add.at(b, (i, j), 1)
pd.Series(c[b.argmax(1)], r)
John White
Tom Blue
Jerry Black
dtype: object
_
groupby
、size
、およびidxmax
_df.groupby(['name', 'color']).size().unstack().idxmax(1)
name
Jerry Black
John White
Tom Blue
dtype: object
name
Jerry Black
John White
Tom Blue
Name: color, dtype: object
_
Counter
¯\_(ツ)_/¯
_from collections import Counter
df.groupby('name').color.apply(lambda c: Counter(c).most_common(1)[0][0])
name
Jerry Black
John White
Tom Blue
Name: color, dtype: object
_
pd.Series.mode
からのソリューション
df.groupby('name').color.apply(pd.Series.mode).reset_index(level=1,drop=True)
Out[281]:
name
Jerry Black
John White
Tom Blue
Name: color, dtype: object
transform(max)
で2つのグループ化を行うのはどうですか?
df = df.groupby(["name", "color"], as_index=False, sort=False).count()
idx = df.groupby("name", sort=False).transform(max)["day"] == df["day"]
df = df[idx][["name", "color"]].reset_index(drop=True)
出力:
name color
0 John White
1 Tom Blue
2 Jerry Black
@piRSquaredの_pd.factorize
_および_np.add.at
_ ansに似ています。
文字列を列にエンコードします
_i, r = pd.factorize(df.name)
j, c = pd.factorize(df.color)
n, m = len(r), len(c)
b = np.zeros((n, m), dtype=np.int64)
_
しかし、これを行う代わりに:
_np.add.at(b, (i, j), 1)
max_columns_after_add_at = b.argmax(1)
_
Jited関数を使用して_max_columns_after_add_at
_を取得し、同じループでat atを追加して最大値を見つけます。
_@nb.jit(nopython=True, cache=True)
def add_at(x, rows, cols, val):
max_vals = np.zeros((x.shape[0], ), np.int64)
max_inds = np.zeros((x.shape[0], ), np.int64)
for i in range(len(rows)):
r = rows[i]
c = cols[i]
x[r, c]+=1
if(x[r, c] > max_vals[r]):
max_vals[r] = x[r, c]
max_inds[r] = c
return max_inds
_
そして最後にデータフレームを取得し、
_ans = pd.Series(c[max_columns_after_add_at], r)
_
したがって、違いはargmax(axis=1) after np.add.at()
の方法です。
タイミング分析
_import numpy as np
import numba as nb
m = 100000
n = 100000
rows = np.random.randint(low = 0, high = m, size=10000)
cols = np.random.randint(low = 0, high = n, size=10000)
_
したがって、この:
_%%time
x = np.zeros((m,n))
np.add.at(x, (rows, cols), 1)
maxs = x.argmax(1)
_
与える:
CPU時間:ユーザー12.4秒、システム:38秒、合計:50.4秒ウォール時間:50.5秒
この
_%%time
x = np.zeros((m,n))
maxs2 = add_at(x, rows, cols, 1)
_
与える
CPU時間:ユーザー108ミリ秒、システム:39.4秒、合計:39.5秒ウォール時間:38.4秒