Группировка по повторяющимся столбцам для объединения их в один столбец с одинаковым именем

#python #pandas

#python #pandas

Вопрос:

Я пытаюсь объединить повторяющиеся столбцы в моем фрейме данных. Мой фрейм данных многоиндексирован и выглядит как

                    sex_COPYL    sex_COPYR    age_COPYL    age_COPYR
ID    Date
 A    2010-01-01        NaN           F            NaN          230
 B    2010-01-01        NaN           F            NaN          487
      2010-02-01        NaN           M            NaN          488
 C    2010-01-01        NaN           M            NaN          534
 D    2012-09-08          M         NaN            432          NaN
  

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

                        sex           age
ID    Date
 A    2010-01-01        F            230
 B    2010-01-01        F            487
      2010-02-01        M            488
 C    2010-01-01        M            534
 D    2012-09-08        M            432
  

Я пытаюсь добиться этого с помощью

 df.groupby(df.columns.map(lambda x: x.split('_COPY')[0], 1)).apply(lambda x: x.mode(1)[0])
  

но я получаю сообщение об ошибке

 ValueError: Grouper and axis must be same length
  

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

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

1. Что мешает вам просто удалить столбец? Я предполагаю, что столбец не обязательно будет all nan . Будут ли конфликтующие данные? Например, M в одном столбце и F в другом? Или разного возраста? Как это следует решить? Или это всегда одно значение и nan ?

2. @busybear Это хороший момент, и это предположение верно. Всегда есть одно значение и nan . Я отредактирую свой вопрос, чтобы отразить это, спасибо, что указали на это!

3. Привет, nebula, print(df.columns) относится к классу «<class’pandas.core.indexes.base. Индекс’>». итак, если вы используете df.groupby(df.columns), это выдаст ошибку, как вы указали выше. для работы вы должны использовать df.groupby(список (df.columns)).

4. Привет @BhanuTez, но я не группирую по полным именам столбцов, я группирую по df.columns.map(...) , что похоже на группировку по ['sex', 'sex', 'age', 'age'] .

5. @HS-nebula Я говорю о причине «ValueError». Но в любом случае в вашем коде также есть проблема с именем столбца.

Ответ №1:

в groupby отсутствует axis= 1:

 df.groupby(df.columns.map(lambda x: x.split('_COPY')[0], 1), axis=1).apply(lambda x: x.mode(1)[0])
  

альтернативное решение (без groupby, но аналогично со stack и unstack):

 df.rename(columns=lambda x: x.split('_COPY')[0]).stack().unstack()
  

метод stack удаляет значения na по умолчанию

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

1. Спасибо, я понял, что у меня была опечатка в строке groupby, поэтому в 1 моем map коде не должно быть a. Кроме того, хорошее альтернативное решение.

Ответ №2:

Альтернативное решение:

 # use both bfill and ffill to handle NaNs on both
# left and right of valid values
df['sex'] = (df.filter(like='sex')
               .bfill(axis=1)
               .ffill(axis=1)
               .iloc[:, 0])

df['age'] = (df.filter(like='age')
               .bfill(axis=1)
               .ffill(axis=1)
               .iloc[:, 0]
               .astype(int))

df = df[['sex', 'age']]
df
                      sex  age
ID         Date
A          2010-01-01   F  230
B          2010-01-01   F  487
           2010-02-01   M  488
C          2010-01-01   M  534
D          2012-09-08   M  432
  

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

1. Спасибо, этот метод работает, но он не объясняет, почему groupby не удалось выполнить то же самое без создания дополнительных столбцов.

2. В качестве бонуса я мог бы сделать это в одной строке, например df.bfill(axis=1).ffill(axis=1).iloc[:, ::2] , а затем переименовать столбцы.

Ответ №3:

Сначала мы можем преобразовать имена столбцов:

 df.columns = [c.split('_COPY')[0] for c in df.columns]
df

Out:
    sex sex age age
ID  Date                
A   2010-01-01  NaN F   NaN 230
B   2010-01-01  NaN F   NaN 487
NaN 2010-02-01  NaN M   NaN 488
C   2010-01-01  NaN M   NaN 534
D   2012-09-08  NaN M   NaN 432
  

Затем группируйте по именам столбцов и режиму использования:

 df.groupby(axis=1, level=0).agg(lambda x: x.mode(axis=1)[0])

Out:
        age sex
ID  Date        
A   2010-01-01  230.0   F
B   2010-01-01  487.0   F
NaN 2010-02-01  488.0   M
C   2010-01-01  534.0   M
D   2012-09-08  432.0   M
  

Обновление: оригинальная однострочная версия также работает с axis=1 добавленными в список groupby параметрами (поскольку мы группируем по столбцам, а не по строкам):

 df.groupby(df.columns.map(lambda x: x.split('_COPY')[0], 1), axis=1).apply(lambda x: x.mode(1)[0])
  

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

1. Итак, все, что мне нужно было сделать, это добавить level=0 в мой groupby ? Можете ли вы объяснить, почему?

2. Проблема, похоже, в том, что в вашем заявлении отсутствует axis=1 (поскольку нам нужно группировать по столбцам, а не по строкам) в качестве параметра groupby. Хорошо работает следующее: df.groupby(df.columns.map(lambda x: x.split('_COPY')[0], 1), axis=1).apply(lambda x: x.mode(1)[0])