迭代熊猫数据框并应用条件函数的更快方法

时间:2019-05-23 08:32:54

标签: python python-3.x pandas dataframe

摘要

我正在尝试遍历大型数据框。根据几列来确定唯一组,然后根据组中有多少列将平均值应用于另一列。当迭代大型数据集并将平均值函数应用于许多列时,我当前的方法非常慢。有什么方法可以更有效地做到这一点?

示例

这是问题的一个例子。我想找到['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。如何更有效地做到这一点

3 个答案:

答案 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)
相关问题