文章目录

  • 前言
  • 一、重塑和透视
  • 1.1 使用多层索引进行重塑
  • 1.2 将"长"透视为"宽"
  • 1.3 将"宽"透视为"长"
  • 总结



前言

这篇文章将介绍数据的重塑和透视操作。昨晚有和舍友讨论过,数据挖掘和数据分析是不是一回事。经过一顿概述学习,数据挖据与数据分析虽然有很多相似之处,但终究还是存在着一定的区别:
1.数据分析讲究的是利用统计分析工具进行观察和处理数据,而数据挖掘是从数据中发现知识规则
2.“数据分析”不能建立数学模型,需要人工建模,而“数据挖掘”直接完成了数学建模,可通过机器学习自动建立输入与输出的函数关系,根据知识规则得出的“规则”,给定一组输入参数,就可以得出一组输出量。
3.“数据分析”主要采用对比分析、分组分析、交叉分析、回归分析等分析方法,通过得到的指标统计量结果,如总和、平均值等,而这些指标数据都需要与业务结合进行解读才能发挥出数据的价值和作用。而“数据挖掘”主要侧重解决分类、聚类、关联和预测四类问题,通过采用决策树、神经网络、关联规则、聚类分析、机器学习等方法进行挖掘,输出模型或规则并且可相应得到模型得分或标签。
跑题,下面进入正文


一、重塑和透视

重排列表格型数据有多种基础操作。这些操作被称为重塑和透视。

1.1 使用多层索引进行重塑

多层索引在DataFrame中提供了一种一致性方式用于重排列数据。以下是两个基础操作:
stack(堆叠):该操作会旋转或将列中的数据透视到行。
unstack(拆堆):该操作会将行中的数据透视到列。

我将通过一系列的例子来说明这些操作。考虑一个带有字符串数组作为行和列索引的小型DataFrame:

data = pd.DataFrame(np.arange(6).reshape((2,3)),
                    index=pd.Index(['Ohio','Colorado'],name='state'),
                    columns=pd.Index(['one','two','three'],name='number'))

print(data)
--------------------------------------------------------------------------
number    one  two  three
state                    
Ohio        0    1      2
Colorado    3    4      5

在这份数据上使用stack方法会将列透视到行、产生一个新的Series:

result = data.stack()
print(result)
--------------------------------------------------------------------------
state     number
Ohio      one       0
          two       1
          three     2
Colorado  one       3
          two       4
          three     5
dtype: int32

从一个多层索引序列中,你可以使用unstack方法将数据重排列放入一个DataFrame中:

result1 = result.unstack()
print(result1)
--------------------------------------------------------------------------
number    one  two  three
state                    
Ohio        0    1      2
Colorado    3    4      5

你可以传入一个层级序号或名称来拆分一个不同的层级:

result1 = result.unstack(0)
print(result1)
result1 = result.unstack('state')
print(result1)
--------------------------------------------------------------------------
state   Ohio  Colorado
number                
one        0         3
two        1         4
three      2         5

state   Ohio  Colorado
number                
one        0         3
two        1         4
three      2         5

如果层级中的所有值并未包含于每个子分组中时,拆分可能会引入缺失值:

s1 = pd.Series([0,1,2,3],index=['a','b','c','d'])
s2 = pd.Series([4,5,6],index=['c','d','e'])
data2 = pd.concat([s1,s2],keys=['one','two'])
print(data2)
a = data2.unstack()
print(a)
--------------------------------------------------------------------------
one  a    0
     b    1
     c    2
     d    3
two  c    4
     d    5
     e    6
dtype: int64

       a    b    c    d    e
one  0.0  1.0  2.0  3.0  NaN
two  NaN  NaN  4.0  5.0  6.0

默认情况下,堆叠会过滤出缺失值,因此堆叠拆堆的操作是可逆的:

a1 = a.stack()
print(a1)
a1 = a.stack(dropna=False)
print(a1)
--------------------------------------------------------------------------
one  a    0.0
     b    1.0
     c    2.0
     d    3.0
two  c    4.0
     d    5.0
     e    6.0
dtype: float64

one  a    0.0
     b    1.0
     c    2.0
     d    3.0
     e    NaN
two  a    NaN
     b    NaN
     c    4.0
     d    5.0
     e    6.0
dtype: float64

当你在DataFrame中拆堆时,被拆堆的层级会变为结果中最低的层级:

df = pd.DataFrame({'left':result,'right':result+5},
                  columns=pd.Index(['left','right'],name='side'))
print(df)
a = df.unstack('state')
print(a)
a1 = a.stack('side')
print(a1)
--------------------------------------------------------------------------
side             left  right
state    number             
Ohio     one        0      5
         two        1      6
         three      2      7
Colorado one        3      8
         two        4      9
         three      5     10

side   left          right         
state  Ohio Colorado  Ohio Colorado
number                             
one       0        3     5        8
two       1        4     6        9
three     2        5     7       10

state         Colorado  Ohio
number side                 
one    left          3     0
       right         8     5
two    left          4     1
       right         9     6
three  left          5     2
       right        10     7

在调用stack方法时,我们可以指定需要堆叠的轴向名称。


1.2 将"长"透视为"宽"

在数据库和CSV中存储多时间序列的方式就是所谓的长格式或堆叠格式。让我们载入一些示例数据,然后做少量的时间序列规整或其他的数据清洗操作:

import pandas as pd
import numpy as np
data = pd.read_csv('examples.csv')
print(data)
periods = pd.PeriodIndex(year=data.year,quarter=data.quarter,name='date')
colums = pd.Index(['realgdp','infl','unemp'],name='item')
data = data.reindex(columns=colums)
data.index = periods.to_timestamp('D','end')
ldata = data.stack().reset_index().rename(columns={0:'value'})

在这本书的第11章会更加详细的讲述PeriodIndex。简单地来说,PeriodIndex将year和quarter等列进行联合并生成了一种时间间隔类型:

a = ldata[:10]
print(a)
------------------------------------------------------------------
                           data     item     value
0 1959-03-31 23:59:59.999999999  realgdp  2710.349
1 1959-03-31 23:59:59.999999999     infl     0.000
2 1959-03-31 23:59:59.999999999    unemp     5.800
3 1959-06-30 23:59:59.999999999  realgdp  2778.801
4 1959-06-30 23:59:59.999999999     infl     2.340
5 1959-06-30 23:59:59.999999999    unemp     5.100
6 1959-09-30 23:59:59.999999999  realgdp  2775.488
7 1959-09-30 23:59:59.999999999     infl     2.740
8 1959-09-30 23:59:59.999999999    unemp     5.300
9 1959-12-31 23:59:59.999999999  realgdp  2785.204

这种数据即所谓的多时间序列的长格式,或称为具有两个或更多个键的其他观测数据(这里,我们的键是date和item)。表中的每一行表示一个时间点上的单个观测值。

数据通常以这种方式存储在关系型数据库中,比如MySQL,因为固定模式(列名称和数据类型)允许item列中不同值的数量随着数据被添加到表中而改变。在某些情况下,处理这种格式的数据更为困难,你可能倾向于获取一个按date列时间戳索引的且每个不同的item独立一列的DataFrame。DataFrame的pivot方法就是进行这种转换的:

pivoted =ldata.pivot('date','item','value')
print(pivoted)
------------------------------------------------------------------
item                           infl   realgdp  unemp
date                                                
1959-03-31 23:59:59.999999999  0.00  2710.349    5.8
1959-06-30 23:59:59.999999999  2.34  2778.801    5.1
1959-09-30 23:59:59.999999999  2.74  2775.488    5.3
1959-12-31 23:59:59.999999999  0.27  2785.204    5.6
1960-03-31 23:59:59.999999999  2.31  2847.699    5.2

传递的前两个值是分别用做行和列的索引,然后是可选的数值列以填充DataFrame。假设你有两个数值列,你想同时进行重塑:

ldata['value2'] = np.random.rand(len(ldata))
print(ldata[:10])
------------------------------------------------------------------
                           date     item     value    value2
0 1959-03-31 23:59:59.999999999  realgdp  2710.349  0.159273
1 1959-03-31 23:59:59.999999999     infl     0.000  0.634771
2 1959-03-31 23:59:59.999999999    unemp     5.800  0.755082
3 1959-06-30 23:59:59.999999999  realgdp  2778.801  0.039249
4 1959-06-30 23:59:59.999999999     infl     2.340  0.348240
5 1959-06-30 23:59:59.999999999    unemp     5.100  0.236757
6 1959-09-30 23:59:59.999999999  realgdp  2775.488  0.268822
7 1959-09-30 23:59:59.999999999     infl     2.740  0.583768
8 1959-09-30 23:59:59.999999999    unemp     5.300  0.823572
9 1959-12-31 23:59:59.999999999  realgdp  2785.204  0.295369

如果遗漏最好一个参数,你会得到一个含有多层列的DataFrame:

pivoted =ldata.pivot('date','item')
print(pivoted)
print(pivoted['value'])

------------------------------------------------------------------
                              value            ...    value2          
item                           infl   realgdp  ...   realgdp     unemp
date                                           ...                    
1959-03-31 23:59:59.999999999  0.00  2710.349  ...  0.955451  0.615201
1959-06-30 23:59:59.999999999  2.34  2778.801  ...  0.514404  0.019166
1959-09-30 23:59:59.999999999  2.74  2775.488  ...  0.250485  0.459031
1959-12-31 23:59:59.999999999  0.27  2785.204  ...  0.888854  0.291041
1960-03-31 23:59:59.999999999  2.31  2847.699  ...  0.789863  0.392345
[5 rows x 6 columns]

item                           infl   realgdp  unemp
date                                                
1959-03-31 23:59:59.999999999  0.00  2710.349    5.8
1959-06-30 23:59:59.999999999  2.34  2778.801    5.1
1959-09-30 23:59:59.999999999  2.74  2775.488    5.3
1959-12-31 23:59:59.999999999  0.27  2785.204    5.6
1960-03-31 23:59:59.999999999  2.31  2847.699    5.2

pivot方法等价于使用set_index创建分层索引,然后调用unstack:

unstacked = ldata.set_index(['date','item']).unstack('item')
print(unstacked)
------------------------------------------------------------------
                              value            ...    value2          
item                           infl   realgdp  ...   realgdp     unemp
date                                           ...                    
1959-03-31 23:59:59.999999999  0.00  2710.349  ...  0.955451  0.615201
1959-06-30 23:59:59.999999999  2.34  2778.801  ...  0.514404  0.019166
1959-09-30 23:59:59.999999999  2.74  2775.488  ...  0.250485  0.459031
1959-12-31 23:59:59.999999999  0.27  2785.204  ...  0.888854  0.291041
1960-03-31 23:59:59.999999999  2.31  2847.699  ...  0.789863  0.392345
[5 rows x 6 columns]

1.3 将"宽"透视为"长"

在DataFrame中,pivot方法的反操作是pandas.melt.与将一列变换为新的DataFrame中的多列不同,它将多列合并成一列,产生一个新的DataFrame,其长度比输入更长。让我们看下面这个例子:

df = pd.DataFrame({'key': ['foo', 'bar', 'baz'],
                   'A': [1, 2, 3],
                   'B': [4, 5, 6],
                   'C': [7, 8, 9]})
print(df)
------------------------------------------------------------------
   key  A  B  C
0  foo  1  4  7
1  bar  2  5  8
2  baz  3  6  9

‘key’列可以作为分组指标,其他列均为数据值。当使用pandas.melt时,我们必须指明哪些列是分组指标。此处,让我们使用‘key’作为唯一的分组指标:

melted = pd.melt(df,['key'])
print(melted)
------------------------------------------------------------------
   key variable  value
0  foo        A      1
1  bar        A      2
2  baz        A      3
3  foo        B      4
4  bar        B      5
5  baz        B      6
6  foo        C      7
7  bar        C      8
8  baz        C      9

使用pivot方法,我们可以将数据重塑回原先布局:

reshaped = melted.pivot('key','variable','value')
print(reshaped)
------------------------------------------------------------------
variable  A  B  C
key              
bar       2  5  8
baz       3  6  9
foo       1  4  7

由于pivot的结果根据作为行标签的列生成了索引,我们可能会想要使用reset_index来将数据回移一列:

a = reshaped.reset_index()
print(a)
------------------------------------------------------------------
variable  key  A  B  C
0         bar  2  5  8
1         baz  3  6  9
2         foo  1  4  7

你也可以指定列的子集作为值列:

a1 = pd.melt(df,id_vars=['key'],value_vars=['A','B'])
print(a1)
------------------------------------------------------------------
   key variable  value
0  foo        A      1
1  bar        A      2
2  baz        A      3
3  foo        B      4
4  bar        B      5
5  baz        B      6

pandas.melt的使用也可以无须任何分组指标:

a = pd.melt(df,value_vars=['A','B','C'])
print(a)
a1 = pd.melt(df,value_vars=['key','A','B'])
print(a1)
------------------------------------------------------------------
  variable  value
0        A      1
1        A      2
2        A      3
3        B      4
4        B      5
5        B      6
6        C      7
7        C      8
8        C      9

  variable value
0      key   foo
1      key   bar
2      key   baz
3        A     1
4        A     2
5        A     3
6        B     4
7        B     5
8        B     6

总结

以上就是今天要讲的内容,本文仅仅简单介绍了数据的重塑和透视操作,重塑可以通过unstack和stack方法来实现,而透视操作可以通过pivot和melt方法来实现。根据目前的章节知识,我们已经拥有了pandas的基础知识,包括数据导入,数据清洗以及按照你自己的想法重新组织数据,我们已经预备好去使用matplotlib进行数据可视化了。