Панды Группируются / Перечисляются в несколько строк

#python #pandas #pandas-groupby

Вопрос:

В этом примере у меня всего 7 столбцов в строке. Я группируюсь по учетной записи и фамилии. Группировка по идентификатору учетной записи и фамилии идентифицирует одно и то же лицо; значения в разных строках Контракта, Адреса, Города и штата представляют новое местоположение для идентификатора/Фамилии учетной записи.

Я хотел бы, чтобы Имя/Фамилия учетной записи были в одной строке рядом с одним или несколькими наборами Контрактов, Адресом, городом и штатом.

Текущие данные выглядят следующим образом:

Договор Учетная запись фамилия имя Адрес Город Государство
622 1234 Питт Штифтик 466 7-я авеню Парковый склон NY
28974 1234 Питт Штифтик 1901 Вайн-стрит Филадельфия па
54122 4321 Брод Генри 93 Стенд Dr Натли nj
622 2345 Родос Пыльный 1 Площадь Публичной библиотеки Стэмфорд КТ
28974 2345 Родос Пыльный 1001 Кингс-хайвей Вишневый холм nj
54122 2345 Родос Пыльный Амстердамский проспект, 444 Верхний Вест-Сайд NY

Хотел бы отобразить данные следующим образом:

Учетная запись фамилия имя Контракт.1 Адрес_1 Город_1 Состояние_1 Контракт_2 Адрес_2 Город_2 State_2 Contract_3 Address_3 City_3 State_3
1234 Питт Штифтик 622 466 7-я авеню Парковый склон NY 28974.0 1901 Вайн-стрит Филадельфия PA
4321 Брод Генри 54122 93 Стенд Dr Натли nj
2345 Родос Пыльный 622 1 Площадь Публичной библиотеки Стэмфорд КТ 28974.0 1001 Кингс-хайвей Вишневый холм NJ 54122.0 444 Amsterdamn Ave Upper West Side NY

Вот что я сделал до сих пор. Шаги 5 и далее я переделывал в течение недели. Есть какие-нибудь предложения?

 # Step 1
import pandas as pd
import numpy as np
# read from "my clipboard"
df = pd.read_clipboard()
df

#Step 2
df['Contract_State'] = (df['Contract'].astype(str)   '|'   df['Address']    '|'   df['City']    '|'   df['State']).str.split()
df['Contract'] = df['Contract'].astype(str)
df['AccountID'] = df['AccountID'].astype(str)

# Step 3 - groupby
df2 = pd.DataFrame(df.groupby(['AccountID', 'Last Name']).Contract_State.apply(list)).reset_index()
df2

# Step 4 - flatten the lists
df2['Contract_State'] = df2['Contract_State'].apply(lambda x: np.array(x).flatten())
df2

# Step 5 - The number of elements in lists each list is always even => /2
num_columns = df2['Contract_State'].apply(len).max()
num_columns 

# Step 6
df3 = pd.DataFrame(list(df2['Contract_State']), columns=columns)
df3

# Step 7 - concatenate df2 with contracts, then drop the column "Contract_State"
df4 = pd.concat([df2, df3], join='inner', axis='columns').drop('Contract_State', axis='columns')
df4
 

Комментарии:

1. Как вы можете видеть, существует много способов изменить форму вашей таблицы, но очевидный трюк заключается groupby в использовании cumcount и.

Ответ №1:

IIUC, я думаю, ты можешь сделать это вот так:

 dfg = df.groupby(['AccountID', 'Last Name', df.groupby(['AccountID', 'Last Name']).cumcount()   1]).first().unstack()
dfg.columns = [f'{i}{j}' for i, j in dfg.columns]
df_out = dfg.sort_index(axis=1, key=lambda x: x.str[-1])
df_out.reset_index()
 

Выход:

    AccountID Last Name  Contract1 First Name1                Address1       City1 State1  Contract2 First Name2            Address2         City2 State2  Contract3 First Name3            Address3            City3 State3
0       1234      Pitt      622.0        Brad             466 7th Ave  Park Slope     NY    28974.0        Brad    1901 Vine Street  Philadelphia     PA        NaN         NaN                 NaN              NaN    NaN
1       2345    Rhodes      622.0       Dusty  1 Public Library Plaze    Stamford     CT    28974.0       Dusty  1001 Kings Highway   Cherry Hill     NJ    54122.0       Dusty  444 Amsterdamn Ave  Upper West Side     NY
2       4321      Ford    54122.0       Henry             93 Booth Dr      Nutley     NJ        NaN         NaN                 NaN           NaN    NaN        NaN         NaN                 NaN              NaN    NaN
 

Комментарии:

1. Это так чисто!

2. @not_speshal Мне нравится твой и Pygirl тоже! 1 от меня на обоих!

3. Как вы сказали, похожие трюки с groupby и cumcount :). Я действительно думаю, что ваш ответ должен быть общепринятым!

4. Большое вам спасибо! Этот код сработал как заклинание! Продолжу изучать кодекс.

Ответ №2:

Попробуйте с groupby и pivot_table :

 df["group"] = df.groupby(["AccountID", "Last Name", "First Name"]).cumcount() 1
output = df.pivot_table(index=["AccountID", "Last Name", "First Name"], 
                        columns='group', 
                        values=['Address', 'City', "State"], 
                        aggfunc='first')
output = output.sort_index(axis=1, level=1)
output.columns = [f"{i}_{j}" for i, j in output.columns]
output = output.reset_index()
 

Ответ №3:

Вы можете попробовать использовать groupby и unstack :

 grp_col = ['AccountID', 'Last Name', 'First Name']
df['num'] = df.groupby(grp_col).cumcount() 1
res = df.set_index([*grp_col, 'num']).unstack('num').sort_index(axis=1, level=1).reset_index()
res.columns = res.columns.map(lambda x: f"{x[0]}{x[1]}")
 

RES:

Учетная запись фамилия имя Контракт 1 Адрес1 Город1 Состояние 1 Контракт 2 Адрес2 Город2 State2 Contract3 Address3 City3 State3
0 1234 Питт Штифтик 622.0 466 7-я авеню Парковый склон NY 28974.0 1901 Вайн-стрит Филадельфия PA NaN NaN NaN NaN
1 2345 Родос Пыльный 622.0 1 Площадь Публичной библиотеки Стэмфорд КТ 28974.0 1001 Кингс-хайвей Вишневый холм NJ 54122.0 444 Amsterdamn Ave Upper West Side NY
2 4321 Брод Генри 54122.0 93 Стенд Dr Натли nj NaN NaN NaN NaN NaN NaN NaN NaN

Ответ №4:

Мы можем передать серию непосредственно в pivot_table with aggfunc first. Используется groupby cumcount для перечисления групповых строк, которые становятся новыми суффиксами столбцов:

 cols = ['AccountID', 'Last Name', 'First Name']
dfp = (
    df.pivot_table(
        index=cols,
        columns=df.groupby(cols).cumcount()   1,
        aggfunc='first'
    ).sort_index(axis=1, level=1, sort_remaining=False)
)
# Collapse Multi-Index
dfp.columns = dfp.columns.map(lambda t: '_'.join(map(str, t)))
dfp = dfp.reset_index()
 

Или с set_index unstack без groubpy first , так как cumcount обеспечивает уникальные столбцы:

 cols = ['AccountID', 'Last Name', 'First Name']
dfw = df.set_index(
    [*cols, df.groupby(cols).cumcount()   1]
).unstack().sort_index(axis=1, level=1, sort_remaining=False)
# Collapse Multi-Index
dfw.columns = dfw.columns.map(lambda t: '_'.join(map(str, t)))
dfu = dfw.reset_index()
 

Любой из вариантов приводит:

    AccountID Last Name First Name               Address_1      City_1  Contract_1 State_1           Address_2        City_2  Contract_2 State_2           Address_3           City_3  Contract_3 State_3
0       1234      Pitt       Brad             466 7th Ave  Park Slope       622.0      NY    1901 Vine Street  Philadelphia     28974.0      PA                 NaN              NaN         NaN     NaN
1       2345    Rhodes      Dusty  1 Public Library Plaze    Stamford       622.0      CT  1001 Kings Highway   Cherry Hill     28974.0      NJ  444 Amsterdamn Ave  Upper West Side     54122.0      NY
2       4321      Ford      Henry             93 Booth Dr      Nutley     54122.0      NJ                 NaN           NaN         NaN     NaN                 NaN              NaN         NaN     NaN
 

Модуль pyjanitor имеет абстракцию для этой операции, pivot_wider которая может скрыть сворачивание мультииндекса и восстановление столбцов индекса:

 # pip install pyjanitor
# conda install pyjanitor -c conda-forge
import janitor
import pandas as pd


cols = ['AccountID', 'Last Name', 'First Name']
dfw = (
    df.add_column(
        'group', df.groupby(cols).cumcount()   1
    ).pivot_wider(
        index=cols,
        names_from='group'
    )
)
 
    AccountID Last Name First Name  Contract_1  Contract_2  Contract_3               Address_1           Address_2           Address_3      City_1        City_2           City_3 State_1 State_2 State_3
0       1234      Pitt       Brad       622.0     28974.0         NaN             466 7th Ave    1901 Vine Street                 NaN  Park Slope  Philadelphia              NaN      NY      PA     NaN
1       2345    Rhodes      Dusty       622.0     28974.0     54122.0  1 Public Library Plaze  1001 Kings Highway  444 Amsterdamn Ave    Stamford   Cherry Hill  Upper West Side      CT      NJ      NY
2       4321      Ford      Henry     54122.0         NaN         NaN             93 Booth Dr                 NaN                 NaN      Nutley           NaN              NaN      NJ     NaN     NaN
 

Существует также абстракция для обработки свертывания мультииндекса janitor.collapse_levels , которую можно использовать с pandas операциями для создания более чистого внешнего вида, не отказываясь от гибкости, предлагаемой pivot_table и sort_index :

 cols = ['AccountID', 'Last Name', 'First Name']
dfp = (
    df.pivot_table(
        index=cols,
        columns=df.groupby(cols).cumcount()   1,
        aggfunc='first'
    ).sort_index(
        axis=1, level=1, sort_remaining=False
    ).collapse_levels(sep='_').reset_index()
)
 

dfp :

    AccountID Last Name First Name               Address_1      City_1  Contract_1 State_1           Address_2        City_2  Contract_2 State_2           Address_3           City_3  Contract_3 State_3
0       1234      Pitt       Brad             466 7th Ave  Park Slope       622.0      NY    1901 Vine Street  Philadelphia     28974.0      PA                 NaN              NaN         NaN     NaN
1       2345    Rhodes      Dusty  1 Public Library Plaze    Stamford       622.0      CT  1001 Kings Highway   Cherry Hill     28974.0      NJ  444 Amsterdamn Ave  Upper West Side     54122.0      NY
2       4321      Ford      Henry             93 Booth Dr      Nutley     54122.0      NJ                 NaN           NaN         NaN     NaN                 NaN              NaN         NaN     NaN