Выровнять столбец со значением типа list, дублируя соответственно значение другого столбца в Pandas
Уважаемые эксперты по пандам силы:
Я пытаюсь реализовать функцию для выравнивания столбца фрейма данных, который имеет элемент типа list, я хочу, чтобы для каждой строки фрейма данных, где столбец имеет элемент типа list, все столбцы, кроме указанного столбца, подлежащего выравниванию, были продублированы, в то время как указанный столбец будет иметь одно из значений в списке.
Следующие примеры иллюстрируют мои требования:
input = DataFrame({'A': [1, 2], 'B': [['a', 'b'], 'c']})
A B
0 1 [a, b]
1 2 c
expected = DataFrame({'A': [1, 1, 2], 'B': ['a', 'b', 'c']}, index=[0, 0, 1])
A B
0 1 a
0 1 b
1 2 c
Я чувствую, что может быть элегантное решение / концепция ради этого, но я борюсь.
Вот моя попытка, которая пока не работает.def flattenColumn(df, column):
'''column is a string of the column's name.
for each value of the column's element (which might be a list), duplicate the rest of columns at the correspdonding row with the (each) value.
'''
def duplicate_if_needed(row):
return concat([concat([row.drop(column, axis = 1), DataFrame({column: each})], axis = 1) for each in row[column][0]])
return df.groupby(df.index).transform(duplicate_if_needed)
В знак признания помощи alko, вот мое тривиальное обобщение решения для работы с более чем 2 столбцами в фрейме данных:
def flattenColumn(input, column):
'''
column is a string of the column's name.
for each value of the column's element (which might be a list),
duplicate the rest of columns at the corresponding row with the (each) value.
'''
column_flat = pandas.DataFrame(
[
[i, c_flattened]
for i, y in input[column].apply(list).iteritems()
for c_flattened in y
],
columns=['I', column]
)
column_flat = column_flat.set_index('I')
return (
input.drop(column, 1)
.merge(column_flat, left_index=True, right_index=True)
)
Единственное ограничение на данный момент состоит в том, что порядок столбцов изменился, столбец сплющился бы в самом правом месте, а не в его исходном положении. Это должно быть возможно исправить.4 ответа:
Я думаю, что простой способ сгладить список списков был бы чистым кодом python, так как этот тип объекта не очень хорошо подходит для pandas или numpy. Так что вы можете сделать это, например, с
>>> b_flat = pd.DataFrame([[i, x] ... for i, y in input['B'].apply(list).iteritems() ... for x in y], columns=list('IB')) >>> b_flat = b_flat.set_index('I')
Расплющив столбец B, вы можете объединить его обратно:
>>> input[['A']].merge(b_flat, left_index=True, right_index=True) A B 0 1 a 0 1 b 1 2 c [3 rows x 2 columns]
Если вы хотите, чтобы индекс был воссоздан, как в вашем ожидаемом результате, вы можете добавить
.reset_index(drop=True)
к последней команде.
Удивительно, что нет более "родного" решения. Поместить ответ от @alko в функцию достаточно просто:
def unnest(df, col, reset_index=False): import pandas as pd col_flat = pd.DataFrame([[i, x] for i, y in df[col].apply(list).iteritems() for x in y], columns=['I', col]) col_flat = col_flat.set_index('I') df = df.drop(col, 1) df = df.merge(col_flat, left_index=True, right_index=True) if reset_index: df = df.reset_index(drop=True) return df
Тогда просто
input = pd.DataFrame({'A': [1, 2], 'B': [['a', 'b'], 'c']}) expected = unnest(input, 'B')
Я думаю, было бы неплохо разрешить неинвестирование нескольких столбцов сразу и обработать возможность вложенного столбца с именем
I
, который бы сломал этот код.