Переместить часть строки в фрейме данных в новую строку

У меня есть фрейм данных (панды), который я хочу преобразовать для отображения. Поэтому я хочу переместить некоторые части кадра данных в новые строки, как показано ниже:

  col1 col2 col_to_shift col_not_to_shift1 col_not_to_shift2
0   a1   a2           a3                a4                a5
1   b1   b2          NaN                b4               NaN
2   c1   c2           c3                c4                c5

Я хотел бы получить следующий фрейм данных, в котором создается новая строка для значения (если нет NaN) в столбце для сдвига (уникального), дублируя описание данных, содержащееся в col1 и col2, и сохраняя столбцы, которые мне не нужны. для смещения, даже если они содержат NaN:

  col1 col2 col_to_shift col_not_to_shift1 col_not_to_shift2
0   a1   a2           a3               NaN               NaN
1   a1   a2          NaN                a4                a4
2   b1   b2          NaN                b4               NaN
3   c1   c2           c3               NaN               NaN
4   c1   c2          NaN                c4                c4

Я попробовал посмотреть pd.shift, но у меня не получилось.

Вот фрагмент кода для создания кадра данных:

data = {"col1": ['a1', 'b1', 'c1'], 'col2': ['a2', 'b2', 'c2'],
        'col_to_shift': ['a3', np.NaN, 'c3'],
        'col_not_to_shift1': ['a4', 'b4', 'c4'],
        'col_not_to_shift2': ['a5', np.NaN, 'c5']}
df = pd.DataFrame(data)

Можете ли вы уточнить, как это будет обобщаться, если у вас будет больше столбцов?

mozway 19.07.2024 09:34

Я пояснил, что это справедливо, потому что мой пример не полностью воспроизвел мой фрейм данных (в столбцах могут быть NaN, которые я не хочу сдвигать).

Arthur 19.07.2024 09:49

спасибо за разъяснение, тогда мой первый подход не сработает, а два других сработают (значок dropna='all' важен)

mozway 19.07.2024 10:03

По-прежнему здорово иметь другие варианты на случай, если чей-то фрейм данных не имеет NaN, как у меня, спасибо

Arthur 19.07.2024 10:07
Почему в 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
4
68
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Код

cols = ['col1', 'col2']
out = pd.concat([
    df[cols + ['col_to_shift']].dropna(),
    df[cols + ['col_not_to_shift']].dropna()
]).sort_index().reset_index(drop=True)

вне:

  col1 col2 col_to_shift col_not_to_shift
0   a1   a2           a3              NaN
1   a1   a2          NaN               a4
2   b1   b2          NaN               b4
3   c1   c2           c3              NaN
4   c1   c2          NaN               c4

Спасибо, это отлично работает, если col_not_to_shift не содержит значения NaN, но это относится к моему фрейму данных, я обновил вопрос, чтобы отразить это.

Arthur 19.07.2024 10:05
Ответ принят как подходящий

Один из вариантов использования конката:

  • разделить DataFrame на две части, сохраняя группировку столбцов для обеих
  • удалите пустые строки с помощью dropna
  • сортируйте строки с помощью sort_values ​​ и стабильного алгоритма сортировки
group = ['col1', 'col2']
cols  = df.columns[df.columns.str.contains('not_to_shift')]

out = (pd.concat([df.drop(columns=cols).dropna(axis=0),
                  df[cols.union(group)].dropna(axis=0)
                 ])
         .sort_values(by=group, kind='stable')
      )

Выход:

  col1 col2 col_to_shift col_not_to_shift
0   a1   a2           a3              NaN
0   a1   a2          NaN               a4
1   b1   b2          NaN               b4
2   c1   c2           c3              NaN
2   c1   c2          NaN               c4

Вариант с использованием индекса как группы (исходный индекс не сохраняется):

group = ['col1', 'col2']
not_shift = ['col_not_to_shift']

tmp = df.set_index(group)
out = (pd.concat([tmp.drop(columns=not_shift), tmp[not_shift]])
         .dropna(how='all').sort_index(kind='stable')
         .reset_index()
      )

Или определив список столбцов для сдвига/не сдвига:

shift = ['col_to_shift']
not_shift = ['col_not_to_shift1', 'col_not_to_shift2']

out = (pd.concat([df.drop(columns=not_shift),
                  df.drop(columns=shift)
                 ])
         .dropna(subset=shift+not_shift, how='all')
         .sort_index(kind='stable')
      )

Выход:

  col1 col2 col_to_shift col_not_to_shift1 col_not_to_shift2
0   a1   a2           a3               NaN               NaN
0   a1   a2          NaN                a4                a5
1   b1   b2          NaN                b4               NaN
2   c1   c2           c3               NaN               NaN
2   c1   c2          NaN                c4                c5

С уточнением в вопросе второй вариант у меня работает так, как и ожидалось.

Arthur 19.07.2024 10:03

попробуй это

df1 = df[~df.col_to_shift.isnull()].drop(columns=['col_not_to_shift1', 'col_not_to_shift2'])
df2 = df[~df.col_to_shift.isnull()].drop(columns='col_to_shift')
pd.concat([df1, df2, df[df.col_to_shift.isnull()]], ignore_index=True).set_index(['col1', 'col2']).sort_index().reset_index()

Попробуй это:

((v:=df.set_index(['col1','col2']))
.set_axis([v.columns != 'col_to_shift',v.columns],axis=1)
.stack(level=0)
.droplevel(-1)
.reindex(v.columns,axis=1)
.reset_index())

Выход:

  col1 col2 col_to_shift col_not_to_shift1 col_not_to_shift2
0   a1   a2           a3               NaN               NaN
1   a1   a2          NaN                a4                a5
2   b1   b2          NaN                b4               NaN
3   c1   c2           c3               NaN               NaN
4   c1   c2          NaN                c4                c5

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

Изменить цвет отображения на основе столбца
Как найти максимальную и минимальную метку времени, когда значение становится ниже минимального порога в pyspark?
Подсчет групп одинаковых значений в одном столбце
Как объединить несколько фреймов данных и суммировать общие значения в столбец
Как сопоставить прогнозы за несколько временных интервалов с фактическими значениями?
Суммирование перестановок в фрейме данных Pandas растет суперэкспоненциально
Найдите значение в столбце, который содержит список, возьмите другое значение из следующего столбца и поместите его в первую таблицу в новый столбец
Python pandas read_sas с параметром размера фрагмента завершается с ошибкой из-за несоответствия индекса
Как я могу заполнить значение на основе другого категориального столбца
Что означает: приведение данных Pandas к numpy dtype объекта. Проверьте входные данные с помощью np.asarray(data) и как это можно решить?