панды: Взрываются (дублируются) по группам

#python #pandas

Вопрос:

У меня есть df, который выглядит так :

 df = pd.DataFrame(data={
    'id': ["idx1"],
    'test_A': ["ABC", ],
    'test_B': ["X"],
    'test_C': ["ABC / XYZ"],
    'test_D': ["ABC / JKL / XYZ"]})
 

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

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

 df = pd.DataFrame(data={
    'id': ["idx1", "idx1", "idx1"],
    'test_A': ["ABC", "X", "X"],
    'test_B': ["X", "X", "X"],
    'test_C': ["ABC", "XYZ", "X"],
    'test_D': ["ABC", "XYZ", "JKL"]})
 

Ответ №1:

Вот альтернатива, использующая вспомогательную функцию:

 def split(df):
    return (df.apply(lambda c: c.str.split(' / '))  # split cells
              .apply(lambda x: x.explode().reset_index(drop=True)) # explode
              .fillna({c: 'X' for c in df.filter(like='test_').columns}) # fill missing test with X
              .ffill() # fill non-test columns
            )

## single row
split(df)

## multiple rows
df.groupby('id').apply(split).droplevel(0)
 

выход:

      id test_A test_B test_C test_D
0  idx1    ABC      X    ABC    ABC
1  idx1      X      X    XYZ    JKL
2  idx1      X      X      X    XYZ
 

вывод на лучшем примере @jezrael:

      id test_A test_B test_C test_D
0  idx1    ABC      X    ABC    ABC
1  idx1      X      X    XYZ    JKL
2  idx1      X      X      X    XYZ
0  idx2    SSD   "ABC     aa    ABC
1  idx2      X    JKL      X    JKL
2  idx2      X    XYZ      X      X
 

Ответ №2:

Образец:

 df = pd.DataFrame(data={
    'id': ["idx1", 'idx0'],
    'test_A': ["ABC",'SSD' ],
    'test_B': ["X", 'ABC / JKL / XYZ'],
    'test_C': ["ABC / XYZ", 'aa'],
    'test_D': ["ABC / JKL / XYZ", 'ABC / JKL']})
 

Идея состоит в том , чтобы сначала изменить DataFrame.melt форму, затем использовать Series.str.split и DataFrame.explode и в последний раз использовать GroupBy.cumcount счетчик со idx столбцом, используемым для сортировки, и в последний раз изменить DataFrame.set_index форму и Series.unstack :

 df1 = df.melt('id', ignore_index=False)
df1['value'] = df1['value'].str.split(' / ')
df1 = df1.explode('value').rename_axis('idx').reset_index()
g = df1.groupby(['idx','id','variable']).cumcount()

df1 = (df1.set_index([g,'idx','id','variable'])['value']
          .unstack(fill_value='X')
          .sort_index(level=1)
          .reset_index(level=2)
          .reset_index(drop=True)
          .rename_axis(None, axis=1))
print (df1)
     id test_A test_B test_C test_D
0  idx1    ABC      X    ABC    ABC
1  idx1      X      X    XYZ    JKL
2  idx1      X      X      X    XYZ
3  idx0    SSD    ABC     aa    ABC
4  idx0      X    JKL      X    JKL
5  idx0      X    XYZ      X      X