在Python数据帧的列中查找group by max并标记它的最快方法是什么?
更新2:我实际上画了2000张,而不是3张 更新:我的df列A是错误的。我修好了 下面我有一个非常大的在Python数据帧的列中查找group by max并标记它的最快方法是什么?,python,pandas,performance,Python,Pandas,Performance,更新2:我实际上画了2000张,而不是3张 更新:我的df列A是错误的。我修好了 下面我有一个非常大的df data = {'A':[11111, 11111, 33333,11111], 'B':[101, 101, 102, 101],'C':[1,2,3,4], 'draw0':[5, 6, 2, 1], 'draw1':[4,3,2,1], 'draw2':[2,3,4,6]} df = pd.DataFrame(data) A B C draw0
df
data = {'A':[11111, 11111, 33333,11111], 'B':[101, 101, 102, 101],'C':[1,2,3,4],
'draw0':[5, 6, 2, 1], 'draw1':[4,3,2,1], 'draw2':[2,3,4,6]}
df = pd.DataFrame(data)
A B C draw0 draw1 draw2
0 11111 101 1 5 4 2
1 11111 101 2 6 3 3
2 33333 102 3 2 2 4
3 11111 101 4 1 1 6
我试图找出哪一个抽签栏在每次抽签中获胜。下面是我目前的尝试,但速度很慢,但效果很好。我觉得应该有一个应用程序的方法或其他方法来加快它
draw_cols = [col for col in df if col.startswith('draw')]
for col in draw_cols:
max_idx = df.groupby(['A', 'B'])[col].idxmax().values
df.loc[max_idx, col] = 1
df.loc[~df.index.isin(max_idx), col] = 0
期望输出:
A B C draw0 draw1 draw2
0 11111 101 1 0 1 0
1 11111 101 2 1 0 0
2 33333 102 3 1 1 1
3 11111 101 4 0 0 1
我生成2000列,如下所示:
def simulateDraw(df, n=2000):
#simulate n drawings from the alpha and beta values and create columns
return pd.concat([df,
df.apply(lambda row: pd.Series(np.random.beta(row.C, row.C, size=n)), axis = 1).add_prefix('draw')],
axis = 1)
检查
draw
列与列的最大值相等的每个组
df.update(df.groupby(['A','B'])[['draw0','draw1','draw2']].apply(lambda x: x.eq(x.max(0))).astype('int'))
df
输出:
微观基准
simulateDraw(df,n=4)的结果
simulateDraw(df,n=50)
的结果(更多的行或列超出了我对colab实例的耐心和RAM)
用于基准测试的代码
import pandas as pd
import numpy as np
import perfplot
def simulateDraw(df, n=2000):
return pd.concat([df,
df.apply(lambda row: pd.Series(np.random.beta(row.C, row.C, size=n)), axis = 1).add_prefix('draw')],
axis = 1)
def makedata(n=1):
data = pd.DataFrame({'A':[11111, 11111, 33333,11111] * n, 'B':[101, 101, 102, 101] * n,'C':[1,2,3,4] * n})
data = simulateDraw(data)
return data
def forloop(df):
draw_cols = [col for col in df if col.startswith('draw')]
for col in draw_cols:
max_idx = df.groupby(['A', 'B'])[col].idxmax().values
df.loc[max_idx, col] = 1
df.loc[~df.index.isin(max_idx), col] = 0
return df
def applyeq(df):
draw_cols = [col for col in df if col.startswith('draw')]
df.update(df.groupby(['A','B'])[draw_cols].apply(lambda x: x.eq(x.max(0))).astype('int'))
return df
def idxmax(df):
draw_cols = [col for col in df if col.startswith('draw')]
max_idx = df.groupby(['A', 'B'])[draw_cols].transform('idxmax')
max_idx['index'] = max_idx.index.values
df.update(max_idx.isin(max_idx['index']).astype(int))
return df
perfplot.show(
setup=makedata,
kernels=[idxmax,applyeq,forloop],
n_range=[2**k for k in range(5,22)],
xlabel='len(df)'
)
检查
draw
列与列的最大值相等的每个组
df.update(df.groupby(['A','B'])[['draw0','draw1','draw2']].apply(lambda x: x.eq(x.max(0))).astype('int'))
df
输出:
微观基准
simulateDraw(df,n=4)的结果
simulateDraw(df,n=50)
的结果(更多的行或列超出了我对colab实例的耐心和RAM)
用于基准测试的代码
import pandas as pd
import numpy as np
import perfplot
def simulateDraw(df, n=2000):
return pd.concat([df,
df.apply(lambda row: pd.Series(np.random.beta(row.C, row.C, size=n)), axis = 1).add_prefix('draw')],
axis = 1)
def makedata(n=1):
data = pd.DataFrame({'A':[11111, 11111, 33333,11111] * n, 'B':[101, 101, 102, 101] * n,'C':[1,2,3,4] * n})
data = simulateDraw(data)
return data
def forloop(df):
draw_cols = [col for col in df if col.startswith('draw')]
for col in draw_cols:
max_idx = df.groupby(['A', 'B'])[col].idxmax().values
df.loc[max_idx, col] = 1
df.loc[~df.index.isin(max_idx), col] = 0
return df
def applyeq(df):
draw_cols = [col for col in df if col.startswith('draw')]
df.update(df.groupby(['A','B'])[draw_cols].apply(lambda x: x.eq(x.max(0))).astype('int'))
return df
def idxmax(df):
draw_cols = [col for col in df if col.startswith('draw')]
max_idx = df.groupby(['A', 'B'])[draw_cols].transform('idxmax')
max_idx['index'] = max_idx.index.values
df.update(max_idx.isin(max_idx['index']).astype(int))
return df
perfplot.show(
setup=makedata,
kernels=[idxmax,applyeq,forloop],
n_range=[2**k for k in range(5,22)],
xlabel='len(df)'
)
这种嵌套列表理解不需要groupby,但可以更快地更新值(它替代了对“
apply lambda
”的需求,该需求应用于每个元素,其中np.where
)。如果您的数据帧
很大,那么它可能会更高效(不过我还没有运行任何性能指标!)
这种嵌套列表理解不需要groupby,但可以更快地更新值(它替代了对“
apply lambda
”的需求,该需求应用于每个元素,其中np.where
)。如果您的数据帧
很大,那么它可能会更高效(不过我还没有运行任何性能指标!)
您的预期输出是什么?确定哪一个绘图列获胜的逻辑是什么?我更新了A列。我的解决方案现在可以工作,但仍然缓慢。其中每个组在同一时间对所有绘图进行矢量化。因此,如果您有相对于组的大型绘图,np。哪里将是更优化的解决方案?您的预期输出是什么?确定哪个绘图列获胜的逻辑是什么?我更新了A列。我的解决方案现在可以工作,但仍然缓慢。其中,对于每个组,在所有绘图上同时进行矢量化。因此,如果您有相对于组的大型绘图,np.where将是更优化的解决方案感谢基准测试代码!!这很有帮助。此解决方案针对每个组进行矢量化。但是,输入数据和组数越大,性能越好。@MichaelSzczesny当我的列实际上是draw0-draw1999时,会怎么样?这会改变事情吗?让我们来看看。谢谢你的基准代码!!这很有帮助。此解决方案针对每个组进行矢量化。但是,输入数据和组数越大,性能越好。@MichaelSzczesny当我的列实际上是draw0-draw1999时,会怎么样?这会改变情况吗?让我们来看看。这个解决方案对于更大的数据帧更快。这个解决方案对于更大的数据帧更快。
out = pd.concat(
[
pd.concat(
[
pd.DataFrame(
np.where(
df.loc[df.B.isin([i]),['draw0','draw1','draw2']]==df.loc[df.B.isin([i]),['draw0','draw1','draw2']].max().to_numpy()[None,:],1,0
)
).reset_index(drop=True),\
df.loc[df.B.isin([i]),['A','B','C']].reset_index(drop=True)
], axis=1, sort=False, ignore_index=True
) for i in df.B.unique()
], axis=0, sort=False, ignore_index=True
)
out.rename(columns = {0:'draw0',1:'draw1',2:'draw2',3:'A',4:'B',5:'C'}, inplace=True)