我正在尝试遍历大型数据框。根据几列来确定唯一组,然后根据组中有多少列将平均值应用于另一列。当迭代大型数据集并将平均值函数应用于许多列时,我当前的方法非常慢。有什么方法可以更有效地做到这一点?
这是问题的一个例子。我想找到['A','B','C']的唯一组合。对于每个唯一的组合,我想要列['D']的值/组中的行数。
编辑: 结果数据框应保留重复的组。但是使用已编辑的列“ D”
import pandas as pd
import numpy as np
import datetime
def time_mean_rows():
# Generate some random data
A = np.random.randint(0, 5, 1000)
B = np.random.randint(0, 5, 1000)
C = np.random.randint(0, 5, 1000)
D = np.random.randint(0, 10, 1000)
# init dataframe
df = pd.DataFrame(data=[A, B, C, D]).T
df.columns = ['A', 'B', 'C', 'D']
tstart = datetime.datetime.now()
# Get unique combinations of A, B, C
unique_groups = df[['A', 'B', 'C']].drop_duplicates().reset_index()
# Iterate unique groups
normalised_solutions = []
for idx, row in unique_groups.iterrows():
# Subset dataframe to the unique group
sub_df = df[
(df['A'] == row['A']) &
(df['B'] == row['B']) &
(df['C'] == row['C'])
]
# If more than one solution, get mean of column D
num_solutions = len(sub_df)
if num_solutions > 1:
sub_df.loc[:, 'D'] = sub_df.loc[:,'D'].values.sum(axis=0) / num_solutions
normalised_solutions.append(sub_df)
# Concatenate results
res = pd.concat(normalised_solutions)
tend = datetime.datetime.now()
time_elapsed = (tstart - tend).seconds
print(time_elapsed)
我知道导致减速的部分是num_solutions>1。如何更有效地做到这一点
答案 0 :(得分:2)
嗯,为什么不使用groupby?
df_res = df.groupby(['A', 'B', 'C'])['D'].mean().reset_index()
答案 1 :(得分:1)
这是对AT_asks回答的补充,后者仅给出了解决方案的第一部分。
有了df.groupby(['A', 'B', 'C'])['D'].mean()
之后,只要我们使用共享相同索引的数据框,就可以使用它来更改原始数据框副本中的列'D'
的值。全局解决方案是:
res = df.set_index(['A', 'B', 'C']).assign(
D=df.groupby(['A', 'B', 'C'])['D'].mean()).reset_index()
这将包含相同的行(即使与OP问题中的res
数据帧的顺序不同。
答案 2 :(得分:0)
这是我找到的解决方案
按照AT的建议使用groupby,然后合并回原始df并删除原始的['D','E']列。不错的加速!
def time_mean_rows():
# Generate some random data
np.random.seed(seed=42)
A = np.random.randint(0, 10, 10000)
B = np.random.randint(0, 10, 10000)
C = np.random.randint(0, 10, 10000)
D = np.random.randint(0, 10, 10000)
E = np.random.randint(0, 10, 10000)
# init dataframe
df = pd.DataFrame(data=[A, B, C, D, E]).T
df.columns = ['A', 'B', 'C', 'D', 'E']
tstart_grpby = timer()
cols = ['D', 'E']
group_df = df.groupby(['A', 'B', 'C'])[cols].mean().reset_index()
# Merge df
df = pd.merge(df, group_df, how='left', on=['A', 'B', 'C'], suffixes=('_left', ''))
# Get left columns (have not been normalised) and drop
drop_cols = [x for x in df.columns if x.endswith('_left')]
df.drop(drop_cols, inplace=True, axis='columns')
tend_grpby = timer()
time_elapsed_grpby = timedelta(seconds=tend_grpby-tstart_grpby).total_seconds()
print(time_elapsed_grpby)