сводная таблица панд для нескольких групп - PullRequest
0 голосов
/ 15 марта 2019

У меня есть кадр данных для панд, например:

%pylab inline

import seaborn as sns
sns.set(color_codes=True)

import pandas as pd
import numpy as np
df = pd.DataFrame({"user_id": [1, 2, 3, 4, 5,
                          6, 7, 8, 9],
    "is_sick": [0, 0, 0, 0, 0,
                          0, 1, 1, 1],
                    "sex": ["male", "female", "male", "female", "female",
                          "male", "male", "female", "female"],
                    "age_group": ["young", "old", "old", "young",
                          "small", "old", "young", "young",
                          "old"],
                    "metric_1": [1, 2, 2, 3, 3, 4, 5, 6, 7]})
df['date'] = '2019-01-01'
df['qcut_metric_1'] = pd.qcut(df.metric_1, [0, .25, .5, .66, .75, .97, 1])

# make some more data
df_2 = df.copy()
df_2['date'] = '2019-02-01'
df = pd.concat([df, df_2])

Теперь я хочу рассчитать процент больных людей на группу / группу [(sex), (age_group), (sex, age_group)] для каждой ячейки метрики.

Обратите внимание, я знаю, что отдельная агрегация, т. Е. Для sex, может выглядеть примерно так:

df['sick_percentage__sex'] = df.groupby(['sex']).is_sick.transform(pd.Series.mean)

Наивная таблица может выглядеть следующим образом:

pd.pivot_table(df, values='sick_percentage__sex', index=['qcut_metric_1', 'sex'], columns=[], aggfunc=np.mean)

будет выглядеть так:

        sick_percentage__sex
qcut_metric_1   sex 
(0.999, 2.0]    female  0.40
male    0.25
(2.0, 3.0]  female  0.40
(3.0, 4.28] male    0.25
(4.28, 5.0] male    0.25
(5.0, 6.76] female  0.40
(6.76, 7.0] female  0.40

Но это не подходит для отображения метрики (qcut_metric_1) и всех когорт по ([(sex), (age_group), (sex, age_group)]) процента заболеваемости.Как это можно адаптировать?Может быть, используется многомерное агрегирование?

Требуемый формат вывода:

qcut_metric_1, cohort, percentage_of_sickness

edit

np.mean, так как функция агрегирования сводных данных может давать искаженные результаты (как среднее из сгруппированных средних значений.не быть коммутативным, если количество пользователей в группе не является постоянным).Поэтому мне нужно использовать взвешенное среднее.Я обновил примерный набор данных.

agg = df.groupby(['sex']).agg({'user_id':pd.Series.nunique, 'is_sick':pd.Series.mean})
agg.columns = ['unique_users', 'sick_percentage__sex']
df = df.merge(agg, on='sex')

теперь предоставляет фрейм данных для ввода в сводную таблицу.

Но теперь я также борюсь с синтаксисом взвешенного среднего:

def wavg(x):
    print(x)
    return np.average(x['sick_percentage__sex'], weights= x['unique_users'])

В качестве сводной таблицы pd.pivot_table (df, values ​​= ['sick_percentage__sex', 'unique_users'], index = ['qcut_metric_1', 'sex'], столбцы = [], aggfunc = wavg)В функцию передается только один ряд (а не оба (значение + вес)).

1 Ответ

0 голосов
/ 15 марта 2019

Возможно, сводная таблица не является правильным способом решения проблемы.

Минимальное решение может выглядеть как приведенный ниже код и повторяться во всех когортах.

Есть ли возможность для более эффективного решения? Мой входной файл имеет размер 120 ГБ для несжатого файла CSV / при сжатии с помощью gzip остается 3 ГБ, что соответствует примерно 35 ГБ требований к памяти для панд.

%pylab inline

import seaborn as sns
sns.set(color_codes=True)

import pandas as pd
import numpy as np
df = pd.DataFrame({"user_id": [1, 2, 3, 4, 5,
                          6, 7, 8, 9],
    "is_sick": [0, 0, 0, 0, 0,
                          0, 1, 1, 1],
                    "sex": ["male", "female", "male", "female", "female",
                          "male", "male", "female", "female"],
                    "age_group": ["young", "old", "old", "young",
                          "small", "old", "young", "young",
                          "old"],
                    "metric_1": [1, 2, 2, 3, 3, 4, 5, 6, 7]})
df['date'] = '2019-01-01'
df['qcut_metric_1'] = pd.qcut(df.metric_1, [0, .25, .5, .66, .75, .97, 1])

# make some more data
df_2 = df.copy()
df_2['date'] = '2019-02-01'
df = pd.concat([df, df_2])
cohorts = [['sex', 'age_group'], ['sex'], ['age_group']]
for cohort in cohorts:
    cohort_name = '_'.join(cohort)
    # print(cohort_name)
    agg = df.groupby(cohort).agg({'user_id':pd.Series.nunique, 'is_sick':pd.Series.mean})
    sick_percentage_column = f'sick_percentage__{cohort_name}'
    agg.columns = ['unique_users', sick_percentage_column]
    merged = df.merge(agg, on=cohort) # INNER (default) JOIN ok, as agg derived from total => no values lost

    groupings = ['qcut_metric_1']
    groupings.extend(cohort)
    result = merged.groupby(groupings).apply(lambda x: np.average(x[sick_percentage_column], weights= x['unique_users'])).reset_index().rename({0:sick_percentage_column}, axis=1)
    display(result)    
...