pandas: группирует, разбивая строковое значение на все строки (столбец) и агрегируя функцию - PullRequest
6 голосов
/ 12 марта 2019

Если у меня есть такой набор данных:

id   person_name                       salary
0    [alexander, william, smith]       45000
1    [smith, robert, gates]            65000
2    [bob, alexander]                  56000
3    [robert, william]                 80000
4    [alexander, gates]                70000

Если мы сложим столбец зарплаты, то получим 316000

Я действительно хочу знать, сколько человек, который назвал «александр, кузнец и т. Д.» (В отдельности), зарабатывает в зарплате, если мы суммируем все зарплаты из его имени для разделения в этом наборе данных (который содержит такое же строковое значение).

выход:

group               sum_salary
alexander           171000 #sum from id 0 + 2 + 4 (which contain 'alexander')
william             125000 #sum from id 0 + 3
smith               110000 #sum from id 0 + 1
robert              145000 #sum from id 1 + 3
gates               135000 #sum from id 1 + 4
bob                  56000 #sum from id 2

как мы видим, сумма столбцов sum_salary не совпадает с исходным набором данных. все, потому что функция требует двойного счета.

Я думал, что это похоже на число строк, но меня смущает то, как мы используем функцию агрегирования. Я попытался создать новый список значений в столбцах person_name, а затем застрял.

Любая помощь приветствуется, большое спасибо

Ответы [ 5 ]

4 голосов
/ 12 марта 2019

Решения, работающие со списками в столбце person_name:

#if necessary
#df['person_name'] = df['person_name'].str.strip('[]').str.split(', ')

print (type(df.loc[0, 'person_name']))
<class 'list'>

Первая идея - использовать defaultdict для хранения sum редактируемых значений в цикле:

from collections import defaultdict

d = defaultdict(int)
for p, s in zip(df['person_name'], df['salary']):
    for x in p:
        d[x] += int(s)

print (d)
defaultdict(<class 'int'>, {'alexander': 171000, 
                            'william': 125000, 
                            'smith': 110000, 
                            'robert': 145000, 
                            'gates': 135000, 
                            'bob': 56000})

И затем:

df1 = pd.DataFrame({'group':list(d.keys()),
                    'sum_salary':list(d.values())})
print (df1)
       group  sum_salary
0  alexander      171000
1    william      125000
2      smith      110000
3     robert      145000
4      gates      135000
5        bob       56000

Другое решение с повторяющимися значениями по длине списков и совокупности sum:

from itertools import chain

df1 = pd.DataFrame({
    'group' : list(chain.from_iterable(df['person_name'].tolist())), 
    'sum_salary' : df['salary'].values.repeat(df['person_name'].str.len())
})

df2 = df1.groupby('group', as_index=False, sort=False)['sum_salary'].sum()
print (df2)
       group  sum_salary
0  alexander      171000
1    william      125000
2      smith      110000
3     robert      145000
4      gates      135000
5        bob       56000
3 голосов
/ 12 марта 2019

Можно сделать кратко с dummies, хотя производительность снизится из-за всех методов .str:

df.person_name.str.join('*').str.get_dummies('*').multiply(df.salary, 0).sum()

#alexander    171000
#bob           56000
#gates        135000
#robert       145000
#smith        110000
#william      125000
#dtype: int64
3 голосов
/ 12 марта 2019

Еще один золь:

df_new=(pd.DataFrame({'person_name':np.concatenate(df.person_name.values),
                  'salary':df.salary.repeat(df.person_name.str.len())}))
print(df_new.groupby('person_name')['salary'].sum().reset_index())


  person_name  salary
0   alexander  171000
1         bob   56000
2       gates  135000
3      robert  145000
4       smith  110000
5     william  125000
2 голосов
/ 12 марта 2019

Я проанализировал это как строки списков, скопировав данные OP и использовав pandas.read_clipboard().Если это действительно так (последовательность строк списков), это решение будет работать:

df = df.merge(df.person_name.str.split(',', expand=True), left_index=True, right_index=True)
df = df[[0, 1, 2, 'salary']].melt(id_vars = 'salary').drop(columns='variable')

# Some cleaning up, then a simple groupby
df.value = df.value.str.replace('[', '')
df.value = df.value.str.replace(']', '')
df.value = df.value.str.replace(' ', '')
df.groupby('value')['salary'].sum()

Вывод:

value
alexander    171000
bob           56000
gates        135000
robert       145000
smith        110000
william      125000
1 голос
/ 12 марта 2019

Еще один способ сделать это - iterrows(). Это не будет таким быстрым решением. Но это работает:

ids = []
names = []
salarys = []

# Iterrate over the rows and extract the names from the lists in person_name column
for ix, row in df.iterrows():
    for name in row['person_name']:
        ids.append(row['id'])
        names.append(name)
        salarys.append(row['salary'])

# Create a new 'unnested' dataframe
df_new = pd.DataFrame({'id':ids,
                       'names':names,
                       'salary':salarys})

# Groupby on person_name and get the sum
print(df_new.groupby('names').salary.sum().reset_index())

выход

       names  salary
0  alexander  171000
1        bob   56000
2      gates  135000
3     robert  145000
4      smith  110000
5    william  125000
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...