Панды - плавить, складывать, изменять форму или столбцы данных MultiIndex на основе нескольких атрибутов из имени столбца
У меня есть Pandas dataframe с данными в очень широкой форме... например:
ID Equipment Function Task exprt_cond1_time exprt_cond2_time exprt_cond1_freq exprt_cond2_freq novce_cond1_time novce_cond2_time novce_cond1_freq novce_cond2_freq
0 eq_type_1 Fxn_a task_1 12 24 0.031 0.055 15 31 0.042 0.059
1 eq_type_1 Fxn_a task_2 10 22 0.028 0.052 12 29 0.039 0.055
2 eq_type_1 Fxn_b task_3 13 25 0.033 0.057 18 34 0.047 0.062
3 eq_type_1 Fxn_b task_4 9 19 0.027 0.051 10 28 0.038 0.054
4 eq_type_2 Fxn_a task_1 14 27 0.036 0.056 16 32 0.043 0.061
5 eq_type_2 Fxn_a task_2 11 26 0.030 0.054 14 30 0.041 0.058
но я хочу преобразовать его в более аккуратный длинный формат, используя текст в метке столбца для создания новых столбцов... например, данные из первой и последней строк из приведенного выше могут выглядеть примерно так:
ID Equipment Function Task Experience Condition Time Freq
0 eq_type_1 Fxn_a task_1 expert cond1 12 0.031
1 eq_type_1 Fxn_a task_1 expert cond2 24 0.055
2 eq_type_1 Fxn_a task_1 novice cond1 15 0.042
3 eq_type_1 Fxn_a task_1 novice cond2 31 0.059
...
16 eq_type_2 Fxn_a task_2 expert cond1 11 0.030
17 eq_type_2 Fxn_a task_2 expert cond2 26 0.054
18 eq_type_2 Fxn_a task_2 novice cond1 14 0.041
19 eq_type_2 Fxn_a task_2 novice cond2 30 0.058
Я не могу найти правильную комбинацию melt / stack / reshape / MultiIndex или других функций перевода, чтобы сделать это эффективно, или без того, чтобы мой код стал уродливым, громоздким и почти нечитаемым. Этот вопрос и этот вопрос близки и помогают мне, но они, кажется, конвертируются только на основе одного атрибута в метке. Буду рад любой помощи или советам от SO сообщества!
3 ответа
Давай попробуем pd.wide_to_long
дважды с переименованием некоторых столбцов сделать все это возможным:
rename_d = {'exprt_cond1_time':'Time_exprt_cond1',
'exprt_cond2_time':'Time_exprt_cond2',
'exprt_cond1_freq':'Freq_exprt_cond1',
'exprt_cond2_freq':'Freq_exprt_cond2',
'novce_cond1_time':'Time_novce_cond1',
'novce_cond2_time':'Time_novce_cond2',
'novce_cond1_freq':'Freq_novce_cond1',
'novce_cond2_freq':'Freq_novce_cond2'}
f = df.rename(columns=rename_d)
df1 = pd.wide_to_long(df, ['Time_exprt','Freq_exprt','Time_novce','Freq_novce'],i=['Equipment','Function','Task'],j='Condition',sep='_',suffix='.')
df1 = df1.reset_index()
df_out = pd.wide_to_long(df1,['Time','Freq'],i=['Equipment','Function','Task','Condition'],j='Experience',sep='_',suffix='').reset_index().drop('ID',axis=1)
Выход:
Equipment Function Task Condition Experience Time Freq
0 eq_type_1 Fxn_a task_1 cond1 exprt 12 0.031
1 eq_type_1 Fxn_a task_1 cond1 novce 15 0.042
2 eq_type_1 Fxn_a task_1 cond2 exprt 24 0.055
3 eq_type_1 Fxn_a task_1 cond2 novce 31 0.059
4 eq_type_1 Fxn_a task_2 cond1 exprt 10 0.028
5 eq_type_1 Fxn_a task_2 cond1 novce 12 0.039
6 eq_type_1 Fxn_a task_2 cond2 exprt 22 0.052
7 eq_type_1 Fxn_a task_2 cond2 novce 29 0.055
8 eq_type_1 Fxn_b task_3 cond1 exprt 13 0.033
9 eq_type_1 Fxn_b task_3 cond1 novce 18 0.047
10 eq_type_1 Fxn_b task_3 cond2 exprt 25 0.057
11 eq_type_1 Fxn_b task_3 cond2 novce 34 0.062
12 eq_type_1 Fxn_b task_4 cond1 exprt 9 0.027
13 eq_type_1 Fxn_b task_4 cond1 novce 10 0.038
14 eq_type_1 Fxn_b task_4 cond2 exprt 19 0.051
15 eq_type_1 Fxn_b task_4 cond2 novce 28 0.054
16 eq_type_2 Fxn_a task_1 cond1 exprt 14 0.036
17 eq_type_2 Fxn_a task_1 cond1 novce 16 0.043
18 eq_type_2 Fxn_a task_1 cond2 exprt 27 0.056
19 eq_type_2 Fxn_a task_1 cond2 novce 32 0.061
20 eq_type_2 Fxn_a task_2 cond1 exprt 11 0.030
21 eq_type_2 Fxn_a task_2 cond1 novce 14 0.041
22 eq_type_2 Fxn_a task_2 cond2 exprt 26 0.054
23 eq_type_2 Fxn_a task_2 cond2 novce 30 0.058
pd.wide_to_long
обрабатывает "одновременное плавление" в пандах. Во-первых, нам нужно переименовать эти столбцы, чтобы заставить работать заглушки в pd.wide_to_long.
Вот моя попытка - я пытался использовать pd.wide_to_long
только однажды:
import re
cols = ['Equipment', 'Function', 'Task']
renamer = lambda c: re.sub(r'([^_]*)_(cond\d+)_(time|freq)', r'\3_\1_\2', c)
r = pd.wide_to_long(df.drop('ID',1).rename(columns=renamer),
stubnames=['time','freq'], i=cols, j='Measures',
sep='_',suffix='(?:exprt|novce)_cond\d+') \
.reset_index()
r[['Experience','Condition']] = r.pop('Measures').str.split('_', expand=True)
Результат:
In [192]: r
Out[192]:
Equipment Function Task time freq Experience Condition
0 eq_type_1 Fxn_a task_1 12 0.031 exprt cond1
1 eq_type_1 Fxn_a task_1 24 0.055 exprt cond2
2 eq_type_1 Fxn_a task_1 15 0.042 novce cond1
3 eq_type_1 Fxn_a task_1 31 0.059 novce cond2
4 eq_type_1 Fxn_a task_2 10 0.028 exprt cond1
5 eq_type_1 Fxn_a task_2 22 0.052 exprt cond2
6 eq_type_1 Fxn_a task_2 12 0.039 novce cond1
7 eq_type_1 Fxn_a task_2 29 0.055 novce cond2
8 eq_type_1 Fxn_b task_3 13 0.033 exprt cond1
9 eq_type_1 Fxn_b task_3 25 0.057 exprt cond2
10 eq_type_1 Fxn_b task_3 18 0.047 novce cond1
11 eq_type_1 Fxn_b task_3 34 0.062 novce cond2
12 eq_type_1 Fxn_b task_4 9 0.027 exprt cond1
13 eq_type_1 Fxn_b task_4 19 0.051 exprt cond2
14 eq_type_1 Fxn_b task_4 10 0.038 novce cond1
15 eq_type_1 Fxn_b task_4 28 0.054 novce cond2
16 eq_type_2 Fxn_a task_1 14 0.036 exprt cond1
17 eq_type_2 Fxn_a task_1 27 0.056 exprt cond2
18 eq_type_2 Fxn_a task_1 16 0.043 novce cond1
19 eq_type_2 Fxn_a task_1 32 0.061 novce cond2
20 eq_type_2 Fxn_a task_2 11 0.030 exprt cond1
21 eq_type_2 Fxn_a task_2 26 0.054 exprt cond2
22 eq_type_2 Fxn_a task_2 14 0.041 novce cond1
23 eq_type_2 Fxn_a task_2 30 0.058 novce cond2
Объяснение:
In [198]: df.drop('ID',1).rename(columns=renamer)
Out[198]:
Equipment Function Task time_exprt_cond1 time_exprt_cond2 freq_exprt_cond1 freq_exprt_cond2 time_novce_cond1 \
0 eq_type_1 Fxn_a task_1 12 24 0.031 0.055 15
1 eq_type_1 Fxn_a task_2 10 22 0.028 0.052 12
2 eq_type_1 Fxn_b task_3 13 25 0.033 0.057 18
3 eq_type_1 Fxn_b task_4 9 19 0.027 0.051 10
4 eq_type_2 Fxn_a task_1 14 27 0.036 0.056 16
5 eq_type_2 Fxn_a task_2 11 26 0.030 0.054 14
time_novce_cond2 freq_novce_cond1 freq_novce_cond2
0 31 0.042 0.059
1 29 0.039 0.055
2 34 0.047 0.062
3 28 0.038 0.054
4 32 0.043 0.061
5 30 0.041 0.058
In [199]: pd.wide_to_long(df.drop('ID',1).rename(columns=renamer),
...: stubnames=['time','freq'], i=cols,j='Measures',
...: sep='_',suffix='(?:exprt|novce)_cond\d+') \
...: .reset_index()
...:
Out[199]:
Equipment Function Task Measures time freq
0 eq_type_1 Fxn_a task_1 exprt_cond1 12 0.031
1 eq_type_1 Fxn_a task_1 exprt_cond2 24 0.055
2 eq_type_1 Fxn_a task_1 novce_cond1 15 0.042
3 eq_type_1 Fxn_a task_1 novce_cond2 31 0.059
4 eq_type_1 Fxn_a task_2 exprt_cond1 10 0.028
5 eq_type_1 Fxn_a task_2 exprt_cond2 22 0.052
6 eq_type_1 Fxn_a task_2 novce_cond1 12 0.039
7 eq_type_1 Fxn_a task_2 novce_cond2 29 0.055
8 eq_type_1 Fxn_b task_3 exprt_cond1 13 0.033
9 eq_type_1 Fxn_b task_3 exprt_cond2 25 0.057
10 eq_type_1 Fxn_b task_3 novce_cond1 18 0.047
11 eq_type_1 Fxn_b task_3 novce_cond2 34 0.062
12 eq_type_1 Fxn_b task_4 exprt_cond1 9 0.027
13 eq_type_1 Fxn_b task_4 exprt_cond2 19 0.051
14 eq_type_1 Fxn_b task_4 novce_cond1 10 0.038
15 eq_type_1 Fxn_b task_4 novce_cond2 28 0.054
16 eq_type_2 Fxn_a task_1 exprt_cond1 14 0.036
17 eq_type_2 Fxn_a task_1 exprt_cond2 27 0.056
18 eq_type_2 Fxn_a task_1 novce_cond1 16 0.043
19 eq_type_2 Fxn_a task_1 novce_cond2 32 0.061
20 eq_type_2 Fxn_a task_2 exprt_cond1 11 0.030
21 eq_type_2 Fxn_a task_2 exprt_cond2 26 0.054
22 eq_type_2 Fxn_a task_2 novce_cond1 14 0.041
23 eq_type_2 Fxn_a task_2 novce_cond2 30 0.058
Один из вариантов — с pivot_longer от pyjanitor:
# pip install pyjanitor
import pandas as pd
import janitor
df.pivot_longer(index = slice('ID','Task'),
names_to = ('Experience', 'Condition', '.value'),
names_sep = '_')
ID Equipment Function Task Experience Condition time freq
0 0 eq_type_1 Fxn_a task_1 exprt cond1 12 0.031
1 1 eq_type_1 Fxn_a task_2 exprt cond1 10 0.028
2 2 eq_type_1 Fxn_b task_3 exprt cond1 13 0.033
3 3 eq_type_1 Fxn_b task_4 exprt cond1 9 0.027
4 4 eq_type_2 Fxn_a task_1 exprt cond1 14 0.036
5 5 eq_type_2 Fxn_a task_2 exprt cond1 11 0.030
6 0 eq_type_1 Fxn_a task_1 exprt cond2 24 0.055
7 1 eq_type_1 Fxn_a task_2 exprt cond2 22 0.052
8 2 eq_type_1 Fxn_b task_3 exprt cond2 25 0.057
9 3 eq_type_1 Fxn_b task_4 exprt cond2 19 0.051
10 4 eq_type_2 Fxn_a task_1 exprt cond2 27 0.056
11 5 eq_type_2 Fxn_a task_2 exprt cond2 26 0.054
12 0 eq_type_1 Fxn_a task_1 novce cond1 15 0.042
13 1 eq_type_1 Fxn_a task_2 novce cond1 12 0.039
14 2 eq_type_1 Fxn_b task_3 novce cond1 18 0.047
15 3 eq_type_1 Fxn_b task_4 novce cond1 10 0.038
16 4 eq_type_2 Fxn_a task_1 novce cond1 16 0.043
17 5 eq_type_2 Fxn_a task_2 novce cond1 14 0.041
18 0 eq_type_1 Fxn_a task_1 novce cond2 31 0.059
19 1 eq_type_1 Fxn_a task_2 novce cond2 29 0.055
20 2 eq_type_1 Fxn_b task_3 novce cond2 34 0.062
21 3 eq_type_1 Fxn_b task_4 novce cond2 28 0.054
22 4 eq_type_2 Fxn_a task_1 novce cond2 32 0.061
23 5 eq_type_2 Fxn_a task_2 novce cond2 30 0.058
The .value
вnames_to
определяет, какие части меток столбцов остаются заголовками, а другие объединяются вExperience
иCondition
столбцы.