Панды — Разнесите несколько столбцов в панд и назначьте значение на основе разнесенного столбца

#python #python-3.x #pandas #dataframe #numpy

Вопрос:

Воспроизводимый пример:

 ex = [{"explode1": ["a", "e", "i"], "word": "US_12", "explode2": []}, 
      {"explode1": [], "word": "US_34", "explode2": ["a", "e", "i"]}, 
      {"explode1": ["a", "e", "i"], "word": "US_56", "explode2": ["o", "u"]}]

df = pd.DataFrame(ex)
 

Дает тебе

         explode1   word   explode2
    0  [a, e, i]  US_12         []
    1         []  US_34  [a, e, i]
    2  [a, e, i]  US_56     [o, u]
 

Вы можете предположить, что существует также столбец explode3 и explode4 (исключен для краткости)

Кадр данных о предполагаемом результате:

    exploded_alphabet   word    exploded_type
0                  a  US_12    explode1
1                  e  US_12    explode1
2                  i  US_12    explode1
3                  a  US_34    explode2
4                  e  US_34    explode2
5                  i  US_34    explode2
6                  a  US_54    explode1
7                  e  US_54    explode1
8                  i  US_54    explode1
9                  o  US_34    explode2
10                 u  US_34    explode2
 

Решение должно быть воспроизводимым с 4 столбцами, а не только с 2, упомянутыми выше (я не включил их в свой пример explode3 и explode4 для той же краткости).

Таким образом, общее количество строк будет равно количеству элементов во всех списках в explode1 , explode2 , explode3 и explode4 сглажено.

Мои усилия:

Честно говоря, я думаю, что должен быть более короткий питонический способ, а не взрывать каждый по отдельности, а затем взрывать те, которые имеют несколько типов.

 df = df.explode("explode1")
df = df.explode("explode2")
 

Вышесказанное неверно. Так как это не приводит к одновременному взрыву строк. Он создает дубликаты, если список не пуст в нескольких столбцах расширения.


Другой-непифонический способ, при котором вы повторяете строки, создаете и назначаете новый столбец — это долго и легко сделать. Но эта проблема, вероятно, была решена по-другому.


Чем мой вопрос отличается от другого вопроса «взорвать несколько столбцов»?:

  1. Взрывая их по отдельности. Каждый элемент в этих столбцах создает новую строку (вероятно, это уже есть в SO).
  2. Присвойте значение в exploded_type — Не уверен, что это было решено на SO в сочетании с 1.

Ответ №1:

Используйте DataFrame.melt до explode для отмены, а затем удалите строки с пропущенными значениями (из пустых списков):

 df = (df.melt('word', value_name='exploded_alphabet', var_name='exploded_type')
        .explode("exploded_alphabet")
        .dropna(subset=['exploded_alphabet'])
        .reset_index(drop=True))
print (df)
     word exploded_type exploded_alphabet
0   US_12      explode1                 a
1   US_12      explode1                 e
2   US_12      explode1                 i
3   US_56      explode1                 a
4   US_56      explode1                 e
5   US_56      explode1                 i
6   US_34      explode2                 a
7   US_34      explode2                 e
8   US_34      explode2                 i
9   US_56      explode2                 o
10  US_56      explode2                 u
 

Ответ №2:

вы можете stack и тогда explode :

 result = df.set_index('word').stack().explode().dropna().reset_index(
    name='exploded_alphabet').rename(columns={'level_1': 'exploded_type'})
 

выход:

      word exploded_type exploded_alphabet
0   US_12      explode1                 a
1   US_12      explode1                 e
2   US_12      explode1                 i
3   US_34      explode2                 a
4   US_34      explode2                 e
5   US_34      explode2                 i
6   US_56      explode1                 a
7   US_56      explode1                 e
8   US_56      explode1                 i
9   US_56      explode2                 o
10  US_56      explode2                 u
 

Производительность:

 
for _ in range(20):
    df = df.append(df)
    
len(df) # 3145728

%%timeit 
(
    df.set_index('word')
    .stack().
    explode().
    dropna().
    reset_index(name='exploded_alphabet').
    rename(columns={'level_1': 'exploded_type'})
)

4.77 s ± 62.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%%timeit
(
     df.melt('word', value_name='exploded_alphabet', var_name='exploded_type')
        .explode("exploded_alphabet")
        .dropna(subset=['exploded_alphabet'])
)
6.68 s ± 224 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

%%timeit
explode_columns = ['explode1', 'explode2']
pd.melt(
    frame=df,
    id_vars='word',
    value_vars=explode_columns,
    var_name='exploded_type',
    value_name='exploded_alphabet'
).explode('exploded_alphabet').dropna()

7.17 s ± 109 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
 

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

1. NOTE: Not tested for large dataframes. — хммм, так в чем же тогда причина таймингов для небольших данных?

2. Хммм… в этом есть смысл! Позвольте мне протестировать большие фреймы данных и добавить результаты. 🙂

Ответ №3:

Вы можете использовать pd.melt , чтобы сложить столбцы, а затем взорвать их.

 explode_columns = ['explode1', 'explode2']
pd.melt(
    frame=df,
    id_vars='word',
    value_vars=explode_columns,
    var_name='exploded_type',
    value_name='exploded_alphabet'
).explode('exploded_alphabet').dropna()
 

Он не сохраняет тот же порядок, что и выше, но строки одинаковы.