Как повернуть фрейм данных?

Nov 07 2017
  • Что такое пивот?
  • Как мне повернуться?
  • Это стержень?
  • Длинный формат в широкий формат?

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

... Но я собираюсь попробовать.


Проблема с существующими вопросами и ответами заключается в том, что часто вопрос сосредоточен на нюансе, который ОП не может обобщить, чтобы использовать ряд существующих хороших ответов. Однако ни один из ответов не дает исчерпывающего объяснения (потому что это непростая задача).

Посмотрите несколько примеров из моего поиска в Google

  1. Как повернуть фрейм данных в Pandas?
  • Хороший вопрос и ответ. Но ответ отвечает только на конкретный вопрос без особых пояснений.
  1. сводная таблица pandas во фрейм данных
  • В этом вопросе OP связан с выходом поворота. А именно как выглядят колонки. OP хотел, чтобы он выглядел как R. Это не очень полезно для пользователей pandas.
  1. панды, поворачивающие фреймворк, повторяющиеся строки
  • Еще один достойный вопрос, но ответ сосредоточен на одном методе, а именно pd.DataFrame.pivot

Поэтому всякий раз, когда кто-то ищет, pivotони получают спорадические результаты, которые, скорее всего, не ответят на их конкретный вопрос.


Настроить

Вы можете заметить, что я явно назвал свои столбцы и соответствующие значения столбцов, чтобы они соответствовали тому, как я собираюсь разворачиваться в ответах ниже.

import numpy as np
import pandas as pd
from numpy.core.defchararray import add

np.random.seed([3,1415])
n = 20

cols = np.array(['key', 'row', 'item', 'col'])
arr1 = (np.random.randint(5, size=(n, 4)) // [2, 1, 2, 1]).astype(str)

df = pd.DataFrame(
    add(cols, arr1), columns=cols
).join(
    pd.DataFrame(np.random.rand(n, 2).round(2)).add_prefix('val')
)
print(df)

     key   row   item   col  val0  val1
0   key0  row3  item1  col3  0.81  0.04
1   key1  row2  item1  col2  0.44  0.07
2   key1  row0  item1  col0  0.77  0.01
3   key0  row4  item0  col2  0.15  0.59
4   key1  row0  item2  col1  0.81  0.64
5   key1  row2  item2  col4  0.13  0.88
6   key2  row4  item1  col3  0.88  0.39
7   key1  row4  item1  col1  0.10  0.07
8   key1  row0  item2  col4  0.65  0.02
9   key1  row2  item0  col2  0.35  0.61
10  key2  row0  item2  col1  0.40  0.85
11  key2  row4  item1  col2  0.64  0.25
12  key0  row2  item2  col3  0.50  0.44
13  key0  row4  item1  col4  0.24  0.46
14  key1  row3  item2  col3  0.28  0.11
15  key0  row3  item1  col1  0.31  0.23
16  key0  row0  item2  col3  0.86  0.01
17  key0  row4  item0  col3  0.64  0.21
18  key2  row2  item2  col0  0.13  0.45
19  key0  row2  item0  col4  0.37  0.70

Вопросы)

  1. Почему я получаю ValueError: Index contains duplicate entries, cannot reshape

  2. Как сделать поворот dfтак, чтобы colзначения были столбцами, rowзначения - индексом, а среднее val0- значениями?

     col   col0   col1   col2   col3  col4
     row                                  
     row0  0.77  0.605    NaN  0.860  0.65
     row2  0.13    NaN  0.395  0.500  0.25
     row3   NaN  0.310    NaN  0.545   NaN
     row4   NaN  0.100  0.395  0.760  0.24
    
  3. Как сделать поворот dfтак, чтобы colзначения были столбцами, rowзначения - индексом, среднее значение val0- значениями и отсутствующими значениями 0?

     col   col0   col1   col2   col3  col4
     row                                  
     row0  0.77  0.605  0.000  0.860  0.65
     row2  0.13  0.000  0.395  0.500  0.25
     row3  0.00  0.310  0.000  0.545  0.00
     row4  0.00  0.100  0.395  0.760  0.24
    
  4. Могу ли я получить что-то еще, кроме mean, например, "может быть" sum?

     col   col0  col1  col2  col3  col4
     row                               
     row0  0.77  1.21  0.00  0.86  0.65
     row2  0.13  0.00  0.79  0.50  0.50
     row3  0.00  0.31  0.00  1.09  0.00
     row4  0.00  0.10  0.79  1.52  0.24
    
  5. Могу ли я делать более одного агрегирования за раз?

            sum                          mean                           
     col   col0  col1  col2  col3  col4  col0   col1   col2   col3  col4
     row                                                                
     row0  0.77  1.21  0.00  0.86  0.65  0.77  0.605  0.000  0.860  0.65
     row2  0.13  0.00  0.79  0.50  0.50  0.13  0.000  0.395  0.500  0.25
     row3  0.00  0.31  0.00  1.09  0.00  0.00  0.310  0.000  0.545  0.00
     row4  0.00  0.10  0.79  1.52  0.24  0.00  0.100  0.395  0.760  0.24
    
  6. Могу ли я агрегировать столбцы с несколькими значениями?

           val0                             val1                          
     col   col0   col1   col2   col3  col4  col0   col1  col2   col3  col4
     row                                                                  
     row0  0.77  0.605  0.000  0.860  0.65  0.01  0.745  0.00  0.010  0.02
     row2  0.13  0.000  0.395  0.500  0.25  0.45  0.000  0.34  0.440  0.79
     row3  0.00  0.310  0.000  0.545  0.00  0.00  0.230  0.00  0.075  0.00
     row4  0.00  0.100  0.395  0.760  0.24  0.00  0.070  0.42  0.300  0.46
    
  7. Можно ли разделить на несколько столбцов?

     item item0             item1                         item2                   
     col   col2  col3  col4  col0  col1  col2  col3  col4  col0   col1  col3  col4
     row                                                                          
     row0  0.00  0.00  0.00  0.77  0.00  0.00  0.00  0.00  0.00  0.605  0.86  0.65
     row2  0.35  0.00  0.37  0.00  0.00  0.44  0.00  0.00  0.13  0.000  0.50  0.13
     row3  0.00  0.00  0.00  0.00  0.31  0.00  0.81  0.00  0.00  0.000  0.28  0.00
     row4  0.15  0.64  0.00  0.00  0.10  0.64  0.88  0.24  0.00  0.000  0.00  0.00
    
  8. Или

     item      item0             item1                         item2                  
     col        col2  col3  col4  col0  col1  col2  col3  col4  col0  col1  col3  col4
     key  row                                                                         
     key0 row0  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.86  0.00
          row2  0.00  0.00  0.37  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.50  0.00
          row3  0.00  0.00  0.00  0.00  0.31  0.00  0.81  0.00  0.00  0.00  0.00  0.00
          row4  0.15  0.64  0.00  0.00  0.00  0.00  0.00  0.24  0.00  0.00  0.00  0.00
     key1 row0  0.00  0.00  0.00  0.77  0.00  0.00  0.00  0.00  0.00  0.81  0.00  0.65
          row2  0.35  0.00  0.00  0.00  0.00  0.44  0.00  0.00  0.00  0.00  0.00  0.13
          row3  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.28  0.00
          row4  0.00  0.00  0.00  0.00  0.10  0.00  0.00  0.00  0.00  0.00  0.00  0.00
     key2 row0  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.40  0.00  0.00
          row2  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.13  0.00  0.00  0.00
          row4  0.00  0.00  0.00  0.00  0.00  0.64  0.88  0.00  0.00  0.00  0.00  0.00
    
  9. Могу ли я агрегировать частоту, с которой столбец и строки встречаются вместе, то есть «перекрестная таблица»?

     col   col0  col1  col2  col3  col4
     row                               
     row0     1     2     0     1     1
     row2     1     0     2     1     2
     row3     0     1     0     2     0
     row4     0     1     2     2     1
    
  10. Как преобразовать DataFrame из длинного в широкий, поворачивая ТОЛЬКО по двум столбцам? Дано,

    np.random.seed([3, 1415])
    df2 = pd.DataFrame({'A': list('aaaabbbc'), 'B': np.random.choice(15, 8)})        
    df2        
       A   B
    0  a   0
    1  a  11
    2  a   2
    3  a  11
    4  b  10
    5  b  10
    6  b  14
    7  c   7
    

    Ожидаемое должно выглядеть примерно так

          a     b    c
    0   0.0  10.0  7.0
    1  11.0  10.0  NaN
    2   2.0  14.0  NaN
    3  11.0   NaN  NaN
    
  11. Как сгладить множественный индекс до единственного индекса после pivot

    Из

       1  2   
       1  1  2        
    a  2  1  1
    b  2  1  0
    c  1  0  0
    

    Чтобы

       1|1  2|1  2|2               
    a    2    1    1
    b    2    1    0
    c    1    0    0
    

Ответы

333 piRSquared Nov 07 2017 at 15:00

Начнем с ответа на первый вопрос:

Вопрос 1

Почему я получаю ValueError: Index contains duplicate entries, cannot reshape

Это происходит из-за того, что pandas пытается переиндексировать объект columnsили indexобъект с повторяющимися записями. Существуют различные методы, с помощью которых можно выполнить поворот. Некоторые из них не подходят для случаев, когда есть дубликаты клавиш, на которых его просят повернуть. Например. Посмотрим pd.DataFrame.pivot. Я знаю , что есть повторяющиеся записи , которые разделяют rowи colзначение:

df.duplicated(['row', 'col']).any()

True

Итак, когда я pivotиспользую

df.pivot(index='row', columns='col', values='val0')

Я получаю указанную выше ошибку. Фактически, я получаю ту же ошибку, когда пытаюсь выполнить ту же задачу с помощью:

df.set_index(['row', 'col'])['val0'].unstack()

Вот список идиом, которые мы можем использовать для поворота

  1. pd.DataFrame.groupby + pd.DataFrame.unstack
    • Хороший общий подход для выполнения практически любого типа поворота.
    • Вы указываете все столбцы, которые будут составлять сводные уровни строк и уровни столбцов в одной группе с помощью. Вы следуете этому, выбирая оставшиеся столбцы, которые вы хотите агрегировать, и функции, которые вы хотите выполнить агрегирование. Наконец, вы unstackвыбираете уровни, которые хотите разместить в индексе столбца.
  2. pd.DataFrame.pivot_table
    • Прославленная версия groupbyс более интуитивным API. Для многих это предпочтительный подход. И это предполагаемый подход разработчиков.
    • Укажите уровень строки, уровни столбца, значения для агрегирования и функции для выполнения агрегирования.
  3. pd.DataFrame.set_index + pd.DataFrame.unstack
    • Удобно и интуитивно понятно для некоторых (включая меня). Не может обрабатывать повторяющиеся сгруппированные ключи.
    • Подобно groupbyпарадигме, мы указываем все столбцы, которые в конечном итоге будут уровнями строк или столбцов, и устанавливаем их в качестве индекса. Затем unstackмы помещаем нужные уровни в столбцы. Если оставшиеся уровни индекса или уровни столбцов не уникальны, этот метод завершится ошибкой.
  4. pd.DataFrame.pivot
    • Очень похоже на set_indexто, что имеет ограничение на дублирование ключа. API также очень ограничен. Он принимает только скалярные значения index, columns, values.
    • Подобно этому pivot_tableметоду, мы выбираем строки, столбцы и значения для поворота. Однако мы не можем агрегировать, и если строки или столбцы не уникальны, этот метод завершится ошибкой.
  5. pd.crosstab
    • Это специализированная версия pivot_tableи в чистом виде представляет собой наиболее интуитивно понятный способ выполнения нескольких задач.
  6. pd.factorize + np.bincount
    • Это очень продвинутая техника, которая очень непонятна, но очень быстра. Его нельзя использовать при любых обстоятельствах, но когда его можно использовать, и вам будет удобно пользоваться им, вы получите вознаграждение за производительность.
  7. pd.get_dummies + pd.DataFrame.dot
    • Я использую это для умного выполнения кросс-табуляции.

Примеры

Для каждого последующего ответа и вопроса я собираюсь ответить на него, используя pd.DataFrame.pivot_table. Затем я предложу альтернативы для выполнения той же задачи.

Вопрос 3

Как сделать поворот dfтак, чтобы colзначения были столбцами, rowзначения - индексом, среднее значение val0- значениями и отсутствующими значениями 0?

  • pd.DataFrame.pivot_table

    • fill_valueне установлен по умолчанию. Я стараюсь установить это правильно. В данном случае я установил это значение 0. Обратите внимание, что я пропустил вопрос 2, поскольку он такой же, как и этот, безfill_value
    • aggfunc='mean'это значение по умолчанию, и мне не нужно было его устанавливать. Я включил это для ясности.

      df.pivot_table(
          values='val0', index='row', columns='col',
          fill_value=0, aggfunc='mean')
      
      col   col0   col1   col2   col3  col4
      row                                  
      row0  0.77  0.605  0.000  0.860  0.65
      row2  0.13  0.000  0.395  0.500  0.25
      row3  0.00  0.310  0.000  0.545  0.00
      row4  0.00  0.100  0.395  0.760  0.24
      
  • pd.DataFrame.groupby

    df.groupby(['row', 'col'])['val0'].mean().unstack(fill_value=0)
    
  • pd.crosstab

    pd.crosstab(
        index=df['row'], columns=df['col'],
        values=df['val0'], aggfunc='mean').fillna(0)
    

Вопрос 4

Могу ли я получить что-то еще, кроме mean, например, "может быть" sum?

  • pd.DataFrame.pivot_table

    df.pivot_table(
        values='val0', index='row', columns='col',
        fill_value=0, aggfunc='sum')
    
    col   col0  col1  col2  col3  col4
    row                               
    row0  0.77  1.21  0.00  0.86  0.65
    row2  0.13  0.00  0.79  0.50  0.50
    row3  0.00  0.31  0.00  1.09  0.00
    row4  0.00  0.10  0.79  1.52  0.24
    
  • pd.DataFrame.groupby

    df.groupby(['row', 'col'])['val0'].sum().unstack(fill_value=0)
    
  • pd.crosstab

    pd.crosstab(
        index=df['row'], columns=df['col'],
        values=df['val0'], aggfunc='sum').fillna(0)
    

Вопрос 5

Могу ли я делать более одного агрегирования за раз?

Обратите внимание, что для pivot_tableи crosstabмне нужно было передать список вызываемых. С другой стороны, groupby.aggон может принимать строки для ограниченного числа специальных функций. groupby.aggтакже использовались бы те же вызываемые объекты, которые мы передали другим, но часто более эффективно использовать имена строковых функций, так как это позволяет повысить эффективность.

  • pd.DataFrame.pivot_table

    df.pivot_table(
        values='val0', index='row', columns='col',
        fill_value=0, aggfunc=[np.size, np.mean])
    
         size                      mean                           
    col  col0 col1 col2 col3 col4  col0   col1   col2   col3  col4
    row                                                           
    row0    1    2    0    1    1  0.77  0.605  0.000  0.860  0.65
    row2    1    0    2    1    2  0.13  0.000  0.395  0.500  0.25
    row3    0    1    0    2    0  0.00  0.310  0.000  0.545  0.00
    row4    0    1    2    2    1  0.00  0.100  0.395  0.760  0.24
    
  • pd.DataFrame.groupby

    df.groupby(['row', 'col'])['val0'].agg(['size', 'mean']).unstack(fill_value=0)
    
  • pd.crosstab

    pd.crosstab(
        index=df['row'], columns=df['col'],
        values=df['val0'], aggfunc=[np.size, np.mean]).fillna(0, downcast='infer')
    

Вопрос 6

Могу ли я агрегировать столбцы с несколькими значениями?

  • pd.DataFrame.pivot_tableмы проходим, values=['val0', 'val1']но мы могли бы полностью отказаться от этого

    df.pivot_table(
        values=['val0', 'val1'], index='row', columns='col',
        fill_value=0, aggfunc='mean')
    
          val0                             val1                          
    col   col0   col1   col2   col3  col4  col0   col1  col2   col3  col4
    row                                                                  
    row0  0.77  0.605  0.000  0.860  0.65  0.01  0.745  0.00  0.010  0.02
    row2  0.13  0.000  0.395  0.500  0.25  0.45  0.000  0.34  0.440  0.79
    row3  0.00  0.310  0.000  0.545  0.00  0.00  0.230  0.00  0.075  0.00
    row4  0.00  0.100  0.395  0.760  0.24  0.00  0.070  0.42  0.300  0.46
    
  • pd.DataFrame.groupby

    df.groupby(['row', 'col'])['val0', 'val1'].mean().unstack(fill_value=0)
    

Вопрос 7

Можно ли разделить на несколько столбцов?

  • pd.DataFrame.pivot_table

    df.pivot_table(
        values='val0', index='row', columns=['item', 'col'],
        fill_value=0, aggfunc='mean')
    
    item item0             item1                         item2                   
    col   col2  col3  col4  col0  col1  col2  col3  col4  col0   col1  col3  col4
    row                                                                          
    row0  0.00  0.00  0.00  0.77  0.00  0.00  0.00  0.00  0.00  0.605  0.86  0.65
    row2  0.35  0.00  0.37  0.00  0.00  0.44  0.00  0.00  0.13  0.000  0.50  0.13
    row3  0.00  0.00  0.00  0.00  0.31  0.00  0.81  0.00  0.00  0.000  0.28  0.00
    row4  0.15  0.64  0.00  0.00  0.10  0.64  0.88  0.24  0.00  0.000  0.00  0.00
    
  • pd.DataFrame.groupby

    df.groupby(
        ['row', 'item', 'col']
    )['val0'].mean().unstack(['item', 'col']).fillna(0).sort_index(1)
    

Вопрос 8

Можно ли разделить на несколько столбцов?

  • pd.DataFrame.pivot_table

    df.pivot_table(
        values='val0', index=['key', 'row'], columns=['item', 'col'],
        fill_value=0, aggfunc='mean')
    
    item      item0             item1                         item2                  
    col        col2  col3  col4  col0  col1  col2  col3  col4  col0  col1  col3  col4
    key  row                                                                         
    key0 row0  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.86  0.00
         row2  0.00  0.00  0.37  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.50  0.00
         row3  0.00  0.00  0.00  0.00  0.31  0.00  0.81  0.00  0.00  0.00  0.00  0.00
         row4  0.15  0.64  0.00  0.00  0.00  0.00  0.00  0.24  0.00  0.00  0.00  0.00
    key1 row0  0.00  0.00  0.00  0.77  0.00  0.00  0.00  0.00  0.00  0.81  0.00  0.65
         row2  0.35  0.00  0.00  0.00  0.00  0.44  0.00  0.00  0.00  0.00  0.00  0.13
         row3  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.28  0.00
         row4  0.00  0.00  0.00  0.00  0.10  0.00  0.00  0.00  0.00  0.00  0.00  0.00
    key2 row0  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.40  0.00  0.00
         row2  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.00  0.13  0.00  0.00  0.00
         row4  0.00  0.00  0.00  0.00  0.00  0.64  0.88  0.00  0.00  0.00  0.00  0.00
    
  • pd.DataFrame.groupby

    df.groupby(
        ['key', 'row', 'item', 'col']
    )['val0'].mean().unstack(['item', 'col']).fillna(0).sort_index(1)
    
  • pd.DataFrame.set_index потому что набор ключей уникален как для строк, так и для столбцов

    df.set_index(
        ['key', 'row', 'item', 'col']
    )['val0'].unstack(['item', 'col']).fillna(0).sort_index(1)
    

Вопрос 9

Могу ли я агрегировать частоту, с которой столбец и строки встречаются вместе, то есть «перекрестная таблица»?

  • pd.DataFrame.pivot_table

    df.pivot_table(index='row', columns='col', fill_value=0, aggfunc='size')
    
        col   col0  col1  col2  col3  col4
    row                               
    row0     1     2     0     1     1
    row2     1     0     2     1     2
    row3     0     1     0     2     0
    row4     0     1     2     2     1
    
  • pd.DataFrame.groupby

    df.groupby(['row', 'col'])['val0'].size().unstack(fill_value=0)
    
  • pd.crosstab

    pd.crosstab(df['row'], df['col'])
    
  • pd.factorize + np.bincount

    # get integer factorization `i` and unique values `r`
    # for column `'row'`
    i, r = pd.factorize(df['row'].values)
    # get integer factorization `j` and unique values `c`
    # for column `'col'`
    j, c = pd.factorize(df['col'].values)
    # `n` will be the number of rows
    # `m` will be the number of columns
    n, m = r.size, c.size
    # `i * m + j` is a clever way of counting the 
    # factorization bins assuming a flat array of length
    # `n * m`.  Which is why we subsequently reshape as `(n, m)`
    b = np.bincount(i * m + j, minlength=n * m).reshape(n, m)
    # BTW, whenever I read this, I think 'Bean, Rice, and Cheese'
    pd.DataFrame(b, r, c)
    
          col3  col2  col0  col1  col4
    row3     2     0     0     1     0
    row2     1     2     1     0     2
    row0     1     0     1     2     1
    row4     2     2     0     1     1
    
  • pd.get_dummies

    pd.get_dummies(df['row']).T.dot(pd.get_dummies(df['col']))
    
          col0  col1  col2  col3  col4
    row0     1     2     0     1     1
    row2     1     0     2     1     2
    row3     0     1     0     2     0
    row4     0     1     2     2     1
    

Вопрос 10

Как преобразовать DataFrame из длинного в широкий, поворачивая ТОЛЬКО по двум столбцам?

Первый шаг - присвоить номер каждой строке - этот номер будет индексом строки этого значения в сводном результате. Это делается с помощью GroupBy.cumcount:

df2.insert(0, 'count', df.groupby('A').cumcount())
df2

   count  A   B
0      0  a   0
1      1  a  11
2      2  a   2
3      3  a  11
4      0  b  10
5      1  b  10
6      2  b  14
7      0  c   7

Второй шаг - использовать вновь созданный столбец в качестве индекса для вызова DataFrame.pivot.

df2.pivot(*df)
# df.pivot(index='count', columns='A', values='B')

A         a     b    c
count                 
0       0.0  10.0  7.0
1      11.0  10.0  NaN
2       2.0  14.0  NaN
3      11.0   NaN  NaN

Вопрос 11

Как сгладить множественный индекс до единственного индекса после pivot

Если columnsввести objectсо строкойjoin

df.columns = df.columns.map('|'.join)

еще format

df.columns = df.columns.map('{0[0]}|{0[1]}'.format) 
6 Ch3steR Jun 05 2020 at 23:08

Чтобы расширить ответ @ piRSquared на другую версию вопроса 10

Вопрос 10.1

DataFrame:

d = data = {'A': {0: 1, 1: 1, 2: 1, 3: 2, 4: 2, 5: 3, 6: 5},
 'B': {0: 'a', 1: 'b', 2: 'c', 3: 'a', 4: 'b', 5: 'a', 6: 'c'}}
df = pd.DataFrame(d)

   A  B
0  1  a
1  1  b
2  1  c
3  2  a
4  2  b
5  3  a
6  5  c

Выход:

   0     1     2
A
1  a     b     c
2  a     b  None
3  a  None  None
5  c  None  None

Использование df.groupbyиpd.Series.tolist

t = df.groupby('A')['B'].apply(list)
out = pd.DataFrame(t.tolist(),index=t.index)
out
   0     1     2
A
1  a     b     c
2  a     b  None
3  a  None  None
5  c  None  None

Или Гораздо лучшая альтернатива использования pd.pivot_tableсdf.squeeze.

t = df.pivot_table(index='A',values='B',aggfunc=list).squeeze()
out = pd.DataFrame(t.tolist(),index=t.index)