Панды: мультииндексная группа

У меня есть следующие данные:

import pandas as pd, numpy as np

data = [['id',    'date',      'b_field',   'a_field'],
        ['XYX',   '01/01/2024',  100,        101],
        ['XYX',   '01/02/2024',  200,        201],
        ['ABC',   '01/01/2024',  300,        301],
        ['ABC',   '01/02/2024',  400,        401]]

id_sort=['ABC', 'XYZ']
field_sort=['a_field', 'b_field']
df = pd.DataFrame(data[1:], columns=data[:1])

Панды: мультииндексная группа

Я хотел бы эффективно преобразовать это так, чтобы если бы я сериализовал это в массив csv или json, это выглядело бы так:

Панды: мультииндексная группа

где:

  1. идентификатор сгруппирован вверху (не повторяется для каждого столбца)
  2. сортировка по идентификатору, а затем по полям
  3. Метка ДАТА в той же строке столбца, что и поле.

До сих пор я пробовал несколько вещей. Ближе всего я подошел к следующему:

df.set_index('date').reindex(pd.MultiIndex.from_product([id_sort, field_sort]),axis=1).reset_index()

Конечно, как видите, есть несколько проблем:

  1. нет значений
  2. метка «дата» находится на верхнем уровне, а не на втором
  3. даты выглядят как кортеж, не знаю почему

Панды: мультииндексная группа

Обновлено: Для № 2, чтобы сдвинуть метку даты вниз, похоже, я могу это сделать (не уверен, что это лучший способ):

df = df.rename(columns = {'date': '', '':'date'})

Обратите внимание, что у вас есть опечатка в данных XYX/XYZ.

mozway 30.04.2024 08:33
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
1
72
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Вот один из подходов:

df = pd.DataFrame(data[1:], columns=data[0])

out = (
    df
    .set_index(['id', 'date'])
    .unstack(0)
    .swaplevel(axis=1)
    .sort_index(axis=1)
    .rename_axis(index=[('date', '')],
                 columns=(None, None))
    .reset_index()
)

Выход

         date     ABC             XYX        
              a_field b_field a_field b_field
0  01/01/2024     301     300     101     100
1  01/02/2024     401     400     201     200

Объяснение

Спасибо. добавление этого в конец выглядит так, как будто оно будет обрабатывать метку «дата»: .reset_index().rename(columns = {'date': '', '':'date'}) Насколько это эффективно? набор данных может состоять из миллиона строк, с которыми я это сделаю?

mike01010 30.04.2024 08:18

Обновлено с возвращением «даты» в качестве первого столбца и цепочкой df.reset_index в конце.

ouroboros1 30.04.2024 08:22

С точки зрения производительности, похоже, между моим решением и решением @mozway не так уж много различий. Я получаю 3,05 (мой) против 3,25 (mozway) на df с формой (1000000, 4), индивидуальными id значениями. Предположим, что одним из потенциальных преимуществ подхода Mozway является то, что он позволяет легко выполнить пользовательскую сортировку на случай, если простой sort_index не подойдет (но в данном случае подойдет :)).

ouroboros1 30.04.2024 08:47

да, я тестировал, и на самом деле производительность между двумя решениями колеблется. между 0,01–0,03 мс. Его решения касаются сортировки. я могу отметить только одно решение. так ценно...

mike01010 30.04.2024 18:13

Не волнуйтесь, удачи в проекте!

ouroboros1 30.04.2024 18:23
Ответ принят как подходящий

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

Лучшим подходом к перемещению «даты» на верхний уровень было бы переименовать_ось с помощью кортежа ('date', '') перед вызовом reset_index:

df = pd.DataFrame(data[1:], columns=data[0])

id_sort=['ABC', 'XYX']
field_sort=['a_field', 'b_field']

idx = pd.MultiIndex.from_product([id_sort, field_sort])

out = (df.pivot(index='date', columns='id').swaplevel(axis=1)
         .reindex(idx, axis=1)
         .rename_axis([('date', '')]).reset_index()
      )

Выход:

         date     ABC             XYX        
              a_field b_field a_field b_field
0  01/01/2024     301     300     101     100
1  01/02/2024     401     400     201     200

одно примечание: для очень больших наборов данных это дает PerformanceWarning: indexing past lexsort depth may impact performance.. Я думаю, мне следует сначала отсортировать по мультииндексу?

mike01010 30.04.2024 19:18

@mike01010 действительно, ты можешь сначала sort_,index,()

mozway 30.04.2024 21:25

Вам нужно использовать Pivot с помощью Lambda:

import pandas as pd
import numpy as np

    data = [['id',    'date',      'b_field',   'a_field'],
            ['XYZ',   '01/01/2024',  100,        101],
            ['XYZ',   '01/02/2024',  200,        201],
            ['ABC',   '01/01/2024',  300,        301],
            ['ABC',   '01/02/2024',  400,        401]]
    
    id_sort = ['ABC', 'XYZ']
    field_sort = ['a_field', 'b_field']
    
    df = pd.DataFrame(data[1:], columns=data[0], index=None)
    
    # Set 'id' as index
    df.set_index('id', inplace=True)
    
    # Sort the index according to id_sort
    df = df.loc[id_sort]
    
    # Reset index to make 'id' a column again
    df.reset_index(inplace=True)
    
    # Melt the DataFrame to reshape it
    df_melted = df.melt(id_vars=['id', 'date'],
                        value_vars=field_sort, var_name='field')
    df.index = df.index + 1
    # Pivot the melted DataFrame to get the desired format
    df_pivot = df_melted.pivot(
        index='date', columns=['id', 'field'], values='value').reset_index()
    
    # Flatten the MultiIndex columns
    df_pivot.columns = df_pivot.columns.map(
        lambda x: '_'.join(map(str, x)) if x[1] else x[0])
    
    print(df_pivot)

Другие вопросы по теме