Более быстрый способ итерации pandas dataframe и применения условной функции - PullRequest
0 голосов
/ 23 мая 2019

Сводка

Я пытаюсь перебрать большой фрейм данных.Определите уникальные группы на основе нескольких столбцов, примените среднее значение к другому столбцу на основе количества в группе.Мой нынешний подход очень медленный при итерации большого набора данных и применении функции усреднения по многим столбцам.Есть ли способ, которым я могу сделать это более эффективно?

Пример

Вот пример проблемы.Я хочу найти уникальные комбинации ['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 ]

2 голосов
/ 23 мая 2019

Хм, почему ты не используешь групповую жизнь?

df_res = df.groupby(['A', 'B', 'C'])['D'].mean().reset_index() 

1 голос
/ 23 мая 2019

Это дополнение к ответу 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()

Это будет содержать те же строки (даже если порядок отличается от того, что в res фрейме данных из вопроса OP.

0 голосов
/ 23 мая 2019

Вот решение, которое я нашел

Использование groupby по предложению AT, затем слияние с исходным 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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...