《利用Python进行数据分析》第八章---数据规整:连接、联合与重塑1


前言

在很多应用中,数据可能分布在多个文件或数据库中,抑或以某种不易于分析的格式进行排列。本章关注于对数据联合、连接以及重排列有用的工具。

首先,将pandas中的分层索引的概念,这个概念在这些操作中被广泛使用。然后深入介绍特定的数据操作。


一、分层索引

分层索引允许你在一个轴向上拥有多个(两个或两个以上)索引层级。笼统地说,分层索引提供了一种在更低维度的形式中处理更高维度数据的方式。下面让我们从一个简单的例子开始,先创建一个Series,以列表的列表(或数组)作为索引:

import pandas as pd
import numpy as np

data = pd.Series(np.random.randn(9),
                 index=[['a', 'a', 'a', 'b', 'b', 'c', 'c', 'd', 'd'],
                        [1, 2, 3, 1, 3, 1, 2, 2, 3]])
print(data)
---------------------------------------------------------
a  1    0.954823
   2   -0.275865
   3    0.028851
b  1   -0.653380
   3    2.047604
c  1    0.674058
   2    1.843968
d  2   -0.219208
   3   -0.438914
dtype: float64

索引中的"间隙"表示“直接使用上面的标签”:

a = data.index
print(a)
---------------------------------------------------------
MultiIndex([('a', 1),
            ('a', 2),
            ('a', 3),
            ('b', 1),
            ('b', 3),
            ('c', 1),
            ('c', 2),
            ('d', 2),
            ('d', 3)],
           )


通过分层索引对象,也可以称为部分索引,允许你简洁地选择出数据的子集:

a = data['b']
print(a)
a = data['b':'c']
print(a)
a = data.loc[['b','d']]
print(a)
---------------------------------------------------------
1   -0.653380
3    2.047604
dtype: float64

b  1   -0.653380
   3    2.047604
c  1    0.674058
   2    1.843968
dtype: float64

b  1   -0.653380
   3    2.047604
d  2   -0.219208
   3   -0.438914
dtype: float64

在“内部”层级中进行选择也是可以的:

a = data.loc[:,2]
print(a)
---------------------------------------------------------
a   -0.275865
c    1.843968
d   -0.219208
dtype: float64

分层索引在重塑数据和数组透视表等分组操作中扮演了重要角色。例如,你可以使用unstack方法将数据在DataFrame中重新排列:

a = data.unstack()
print(a)
---------------------------------------------------------
        1         2         3
a  0.954823 -0.275865  0.028851
b -0.653380       NaN  2.047604
c  0.674058  1.843968       NaN
d       NaN -0.219208 -0.438914

unstack的反操作是stack

b = a.stack()
print(b)
---------------------------------------------------------
a  1    0.954823
   2   -0.275865
   3    0.028851
b  1   -0.653380
   3    2.047604
c  1    0.674058
   2    1.843968
d  2   -0.219208
   3   -0.438914
dtype: float64

在DataFrame中,每个轴都可以拥有分层索引:

data = pd.DataFrame(np.arange(12).reshape((4, 3)),
                    index=[['a', 'a', 'b', 'b'], [1, 2, 1, 2]],
                    columns=[['Ohio', 'Ohio', 'Colorado'], ['Green', 'Red', 'Green']])

print(data)
---------------------------------------------------------
     Ohio     Colorado
    Green Red    Green
a 1     0   1        2
  2     3   4        5
b 1     6   7        8
  2     9  10       11

分层的层级可以有名称(可以是字符串或Python对象)。如果层级有名称,这些名称会在控制台输出中显示:

data.index.names=['key1','key2']
data.columns.names = ['state','color']
print(data)
---------------------------------------------------------
state      Ohio     Colorado
color     Green Red    Green
key1 key2                   
a    1        0   1        2
     2        3   4        5
b    1        6   7        8
     2        9  10       11

通过部分列索引,你可以选出列中的组:

a = data['Ohio']
print(a)
---------------------------------------------------------
color      Green  Red
key1 key2            
a    1         0    1
     2         3    4
b    1         6    7
     2         9   10

1.1 重排序和层级排序

有时,你需要重新排列轴上的层级顺序,或者按照特定层级的值对数据进行排序。swaplevel接收两个层级序号或层级名称,返回一个进行了层级变更的新对象(但是数据是不变的):

a = data.swaplevel('key1','key2')
print(a)
---------------------------------------------------------
state      Ohio     Colorado
color     Green Red    Green
key2 key1                   
1    a        0   1        2
2    a        3   4        5
1    b        6   7        8
2    b        9  10       11

另一方面,sort_index只能在单一层级上对数据进行排序。在进行层级变换时,使用sort_index以使得结果按照层级进行字典排序也很常见:

a = data.sort_index(level=1)
print(a)
a = data.swaplevel(0,1).sort_index(level=0)
print(a)
---------------------------------------------------------
state      Ohio     Colorado
color     Green Red    Green
key1 key2                   
a    1        0   1        2
b    1        6   7        8
a    2        3   4        5
b    2        9  10       11

state      Ohio     Colorado
color     Green Red    Green
key2 key1                   
1    a        0   1        2
     b        6   7        8
2    a        3   4        5
     b        9  10       11

如果索引按照字典顺序从最外层开始排序,那么数据选择性能会更好——调用sort_index(level=0)或 sort_index可以得到这样的结果。

1.2 按层级进行汇总统计

DataFrame和Series中很多描述性和汇总性统计有一个level选项,通过level选项你可以指定你想要在某个特定的轴上进行聚合。考虑上述示例中的DataFrame,我们可以按照层级在行或列上像下面这样进行聚合:

print(data)
a = data.sum(level= 'key2')
print(a)
a = data.sum(level= 'color',axis=1)
print(a)
---------------------------------------------------------
state      Ohio     Colorado
color     Green Red    Green
key1 key2                   
a    1        0   1        2
     2        3   4        5
b    1        6   7        8
     2        9  10       11
     
state  Ohio     Colorado
color Green Red    Green
key2                    
1         6   8       10
2        12  14       16

color      Green  Red
key1 key2            
a    1         2    1
     2         8    4
b    1        14    7
     2        20   10

例如a = data.sum(level= ‘key2’),在多层级的数值相加时,先是对应外层“key2”,在对应内层索引“1”和“2”分别求和。**a = data.sum(level= ‘color’,axis=1)**同理,他是先根据外层“color”找到对应的列“Green”和“Red”,而axis=1是列对应列相加。第一列的“Green”找到第二列的“Green”对应相加,而”Red“只有一列,因此保持原样。


1.3 使用DataFrame的列进行索引

通常,我们不会使用DataFrame中一个或多个列作为行索引;反而你可能想要将行索引移动到DataFrame的列中。

data = pd.DataFrame({"a": range(7), "b": range(7, 0, -1),
                     "c": ['one', 'one', 'one', 'two', 'two', 'two', 'two'],
                     "d":[0,1,2,0,1,2,3]})

print(data)
---------------------------------------------------------
   a  b    c  d
0  0  7  one  0
1  1  6  one  1
2  2  5  one  2
3  3  4  two  0
4  4  3  two  1
5  5  2  two  2
6  6  1  two  3

DataFrame的set_index函数会生成一个新的DataFrame,新的DataFrame使用一个或多个列作为索引:

a = data.set_index(['c','d'])
print(a)
---------------------------------------------------------
       a  b
c   d      
one 0  0  7
    1  1  6
    2  2  5
two 0  3  4
    1  4  3
    2  5  2
    3  6  1

默认情况下,这些列会从DataFrame中被删除,但你也可以通过传递参数drop= False 保留它们。

a1 = data.set_index(['c','d'],drop=False)
print(a1)
---------------------------------------------------------
       a  b    c  d
c   d              
one 0  0  7  one  0
    1  1  6  one  1
    2  2  5  one  2
two 0  3  4  two  0
    1  4  3  two  1
    2  5  2  two  2
    3  6  1  two  3

另一方面,reset_indexset_index的反操作,分层索引的索引层级会被移动到列中:

b = a.reset_index()
print(b)
---------------------------------------------------------
     c  d  a  b
0  one  0  0  7
1  one  1  1  6
2  one  2  2  5
3  two  0  3  4
4  two  1  4  3
5  two  2  5  2
6  two  3  6  1

二、联合与合并数据集

包含pandas对像的数据可以通过多种方式联合在一起:

  1. pandas.merge 根据一个或多个键将行进行对接。它实现的是数据库的连接操作。
  2. pandas.concat 使对象在轴向上进行黏合或“堆叠”
  3. combine_first 实例方法允许将重叠的数据拼接在一起,以使用一个对象中的值填充另一个对象中的缺失值。

2.1 数据库风格的DataFrame连接

合并或连接操作通过一个或多个键连接行来联合数据集。这些操作是关系型数据库的核心内容。pandas中的merge函数主要用于将各种join操作算法运用在你的数据上:

data = pd.DataFrame({'key': ['b', 'b', 'a', 'c', 'a', 'a', 'b'],
                     'data1': range(7)})
data1 = pd.DataFrame({'key': ['a', 'b', 'd'],
                      'data2': range(3)})

print(data)
print(data1)
---------------------------------------------------------
  key  data1
0   b      0
1   b      1
2   a      2
3   c      3
4   a      4
5   a      5
6   b      6

  key  data2
0   a      0
1   b      1
2   d      2

这是一个多对一连接的例子;data的数据有多个行的标签为a和b,而data1在key列中每个值仅有一行。调用merge处理我们获得的对象:

a = pd.merge(data,data1)
print(a)
---------------------------------------------------------
  key  data1  data2
0   b      0      1
1   b      1      1
2   b      6      1
3   a      2      0
4   a      4      0
5   a      5      0

我们并没有指定在哪一列上进行连接。如果连接的键信息没有指定,merge会自动将重叠列名作为连接的键。但是,显示地指定连接键才是好的实现:

a = pd.merge(data,data1,on='key')
print(a)
---------------------------------------------------------
  key  data1  data2
0   b      0      1
1   b      1      1
2   b      6      1
3   a      2      0
4   a      4      0
5   a      5      0

如果每个对象的列名是不同的,你可以分别为它们指定列名:

data2 = pd.DataFrame({'lkey': ['b', 'b', 'a', 'c', 'a', 'a', 'b'],
                     'data1': range(7)})
data3 = pd.DataFrame({'rkey': ['a', 'b', 'd'],
                      'data2': range(3)})

a = pd.merge(data2,data3,left_on='lkey',right_on='rkey')
print(a)
---------------------------------------------------------
  lkey  data1 rkey  data2
0    b      0    b      1
1    b      1    b      1
2    b      6    b      1
3    a      2    a      0
4    a      4    a      0
5    a      5    a      0

你可能注意到结果中缺少’c’和’d’的值以及相关的数据。默认情况下,merge做的是内连接(‘inner’ join),结果中的键是两张表的交集。其他可选的选项有’left’、‘right’和’outer’。外连接(outer join)是键的并集,联合了左连接和右连接的效果:

a = pd.merge(data,data1,how='outer')
print(a)
---------------------------------------------------------
  key  data1  data2
0   b    0.0    1.0
1   b    1.0    1.0
2   b    6.0    1.0
3   a    2.0    0.0
4   a    4.0    0.0
5   a    5.0    0.0
6   c    3.0    NaN
7   d    NaN    2.0

下表是对how选项的总结:

选项行为
inner只对两张表都有的键的交集进行联合
left对所有左表的键进行联合
right对所有右表的键进行联合
outer对两张表都有的键的并集进行联合

尽管不是很直观,但多对多的合并有明确的行为。

data = pd.DataFrame({'key': ['b', 'b', 'a', 'c', 'a', 'b'],
                     'data1': range(6)})
data1 = pd.DataFrame({'key': ['a', 'b', 'a', 'b', 'd'],
                      'data2': range(5)})

print(data)
print(data1)

a = pd.merge(data,data1,on='key',how='left')
print(a)
---------------------------------------------------------
  key  data1
0   b      0
1   b      1
2   a      2
3   c      3
4   a      4
5   b      5

  key  data2
0   a      0
1   b      1
2   a      2
3   b      3
4   d      4

   key  data1  data2
0    b      0    1.0
1    b      0    3.0
2    b      1    1.0
3    b      1    3.0
4    a      2    0.0
5    a      2    2.0
6    c      3    NaN
7    a      4    0.0
8    a      4    2.0
9    b      5    1.0
10   b      5    3.0

多对多连接是行的笛卡尔积。由于在左边的DataFrame中有三个’b’行,而在右边有两个行,因此在结果中有六个’b’行。连接方式仅影响结果中显示的不同键:

a = pd.merge(data,data1,how='inner')
print(a)
---------------------------------------------------------
  key  data1  data2
0   b      0      1
1   b      0      3
2   b      1      1
3   b      1      3
4   b      5      1
5   b      5      3
6   a      2      0
7   a      2      2
8   a      4      0
9   a      4      2

补充:

  1. left :在两张表进行连接联合时,会返回左表所有键的行数据,右表中返回只返回和左表键匹配的数据,没有的显示为Null。
  2. right :在两张表进行连接联合时,会返回右表所有键的行数据,左表中返回只返回和右表键匹配的数据,没有的显示为Null。
  3. 笛卡尔积:显示ab的数据集。比如对于a和b都有相同的键时。该键在a表有2条数据,在b表有3条数据,那么最后的结果有23=6条数据。

使用多个键进行合并时,传入一个列名的列表:

left = pd.DataFrame({'key1': ['foo', 'foo', 'bar'],
                     'key2': ['one', 'two', 'one'],
                     'lval': [1, 2, 3]})
right = pd.DataFrame({'key1': ['foo', 'foo', 'bar','bar'],
                     'key2': ['one', 'one', 'one','two'],
                     'rval': [4,5,6,7]})
print(left)
print(right)
a = pd.merge(left,right,on=['key1','key2'],how='outer
print(a)
---------------------------------------------------------
  key1 key2  lval
0  foo  one     1
1  foo  two     2
2  bar  one     3

  key1 key2  lval
0  foo  one     4
1  foo  one     5
2  bar  one     6
3  bar  two     7

  key1 key2  lval   rval
0  foo  one   1.0    4.0
1  foo  one   1.0    5.0
2  foo  two   2.0    NaN
3  bar  one   3.0    6.0
4  bar  two   NaN    7.0

要决定哪些键联合出现在结果中,取决于合并方法的选择,把多个键看作一个元组数据来作为单个连接键使用。

合并操作中最后一个要考虑的问题是如何处理重叠的列名。虽然你可以手动解决重叠问题,但是merge有一个suffixes后缀选项,用于在左右两边DataFrame对象重叠列名后指定需要添加的字符串:

a1 = pd.merge(left, right, on='key1')
print(a1)
a2 = pd.merge(left, right, on='key1', suffixes=('_left', '_right'))
print(a2)
--------------------------------------------------------------------------
  key1 key2_x  rval key2_y  lval
0  foo    one     1    one     4
1  foo    one     1    one     5
2  foo    two     2    one     4
3  foo    two     2    one     5
4  bar    one     3    one     6
5  bar    one     3    two     7

  key1 key2_left  rval key2_right  lval
0  foo       one     1        one     4
1  foo       one     1        one     5
2  foo       two     2        one     4
3  foo       two     2        one     5
4  bar       one     3        one     6
5  bar       one     3        two     7

下表是merge方法的参数参考。使用DataFrame的行索引进行连接是下一节的内容。

参数描述
left合并时操作中左边的DataFrame
right合并时操作中右边的DataFrame
how‘inner’,‘outer’,‘left’,‘right’之一;默认是‘inner’
on需要连接的列名。必须是在两边的DataFrame对象都有的列名,并以left和right中的列名的交集作为连接键的列
left_onleft DataFrame中用作连接键的列
right_onright DataFrame中用作连接键的列
sort通过连接键按字母顺序对合并的数据进行排序;默认情况下为True
suffixes在重叠情况下,添加到列名后的字符串元组;默认是(‘_x’,‘_y’)
copy如果为False,则在某些特殊情况下避免将数据复制到结果数据结构中,默认情况下总是复制
indicator添加一个特殊的列_merge,指示每一行的来源;值将根据每行中连接数据的来源分别为’left_only’,'right_only’或’both‘
left_index使用left的行索引作为他的连接键(如果是MultiIndex,则是多个键)
right_index使用left的行索引作为他的连接键(如果是MultiIndex,则是多个键)

2.2 根据索引合并

在某些情况下,DataFrame中用于合并的键是它的索引。在这种情况下,你可以传递left_index=True或right_index=True(或者都传)来表示索引需要用来作为合并的键:

left1 = pd.DataFrame({'key': ['a', 'b', 'a', 'a', 'b', 'c'],
                      'value': range(6)})
right1 = pd.DataFrame({'group_val': [3.5, 7]},
                      index=['a', 'b'])
print(left1)
print(right1)

a = pd.merge(left1,right1,left_on='key',right_index=True)
print(a)
---------------------------------------------------------
  key  value
0   a      0
1   b      1
2   a      2
3   a      3
4   b      4
5   c      5

   group_val
a        3.5
b        7.0

  key  value  group_val
0   a      0        3.5
2   a      2        3.5
3   a      3        3.5
1   b      1        7.0
4   b      4        7.0

由于默认的合并方法是连接键相交,你可以使用外连接来进行合并:

a1 = pd.merge(left1,right1,left_on='key',right_index=True,how='outer')
print(a1)
---------------------------------------------------------
  key  value  group_val
0   a      0        3.5
2   a      2        3.5
3   a      3        3.5
1   b      1        7.0
4   b      4        7.0
5   c      5        NaN

而在多层索引数据的情况下,事情会更加复杂,在索引上连接是一个隐式的多键合并:

lefth = pd.DataFrame({"key1": ['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada'],
                      'key2': [2000, 2001, 2002, 2001, 2002],
                      'data': np.arange(5.)})

righth = pd.DataFrame(np.arange(12).reshape((6, 2)),
                      index=[['Nevada', 'Nevada', 'Ohio', 'Ohio', 'Ohio', 'Ohio'],
                             [2001,2000,2000,2000,2001,2002]],
                      columns=['event1','event2'])

print(lefth)
print(righth)

---------------------------------------------------------
     key1  key2  data
0    Ohio  2000   0.0
1    Ohio  2001   1.0
2    Ohio  2002   2.0
3  Nevada  2001   3.0
4  Nevada  2002   4.0

             event1  event2
Nevada 2001       0       1
       2000       2       3
Ohio   2000       4       5
       2000       6       7
       2001       8       9
       2002      10      11

这种情况下,你必须以列表的方式指明合并所需多个列(请注意使用how='outer’处理两个表的并集):

a = pd.merge(lefth,righth,left_on=['key1','key2'],right_index=True)
print(a)
a = pd.merge(lefth,righth,left_on=['key1','key2'],right_index=True,how='outer')
print(a)
---------------------------------------------------------
     key1  key2  data  event1  event2
0    Ohio  2000   0.0       4       5
0    Ohio  2000   0.0       6       7
1    Ohio  2001   1.0       8       9
2    Ohio  2002   2.0      10      11
3  Nevada  2001   3.0       0       1

     key1  key2  data  event1  event2
0    Ohio  2000   0.0     4.0     5.0
0    Ohio  2000   0.0     6.0     7.0
1    Ohio  2001   1.0     8.0     9.0
2    Ohio  2002   2.0    10.0    11.0
3  Nevada  2001   3.0     0.0     1.0
4  Nevada  2002   4.0     NaN     NaN
4  Nevada  2000   NaN     2.0     3.0

使用两边的索引进行合并也是可以的:

left2 = pd.DataFrame([[1.,2.],[3.,4.],[5.,6.]],
                     index=['a','c','e'],
                     columns=['Ohio','Nevada'])
right2 = pd.DataFrame([[7.,8.],[9.,10.],[11.,12.],[13.,14.]],
                      index=['b','c','d','e'],
                      columns=['Missouri','Alabama'])

print(left2)
print(right2)

a = pd.merge(left2,right2,how='outer',left_index=True,right_index=True)
print(a)
---------------------------------------------------------
  Ohio  Nevada
a   1.0     2.0
c   3.0     4.0
e   5.0     6.0

   Missouri  Alabama
b       7.0      8.0
c       9.0     10.0
d      11.0     12.0
e      13.0     14.0

   Ohio  Nevada  Missouri  Alabama
a   1.0     2.0       NaN      NaN
b   NaN     NaN       7.0      8.0
c   3.0     4.0       9.0     10.0
d   NaN     NaN      11.0     12.0
e   5.0     6.0      13.0     14.0

DataFrame有一个方便的join实例方法,用于按照索引合并。该方法有可以用于合并多个索引相同或相似但没有重复列的DataFrame对象。在上一个例子中我们可以这样写:

a = left2.join(right2,how='outer')
print(a)
---------------------------------------------------------
   Ohio  Nevada  Missouri  Alabama
a   1.0     2.0       NaN      NaN
b   NaN     NaN       7.0      8.0
c   3.0     4.0       9.0     10.0
d   NaN     NaN      11.0     12.0
e   5.0     6.0      13.0     14.0

它还支持在调用DataFrame的某一列上连接传递的DataFrame的索引:

left1 = pd.DataFrame({'key': ['a', 'b', 'a', 'a', 'b', 'c'],
                      'value': range(6)})
right1 = pd.DataFrame({'group_val': [3.5, 7]},
                      index=['a', 'b'])
print(left1)
print(right1)

a = left1.join(right1,on='key')
print(a)
---------------------------------------------------------
  key  value
0   a      0
1   b      1
2   a      2
3   a      3
4   b      4
5   c      5

   group_val
a        3.5
b        7.0

  key  value  group_val
0   a      0        3.5
1   b      1        7.0
2   a      2        3.5
3   a      3        3.5
4   b      4        7.0
5   c      5        NaN

最后对于一些简单索引-索引合并,你可以向join方法传入一个DataFrame列表,这个方法可以替代下一节中将要介绍的使用更为通用的concat函数的方法:

another = pd.DataFrame([[7.,8.],[9.,10.],[11.,12.],[16.,17.]],
                       index=['a','c','e','f'],
                       columns=['New York','Oregon'])
print(another)
a = left2.join([right2,another])
print(a)
a = left2.join([right2,another],how='outer')
print(a)
---------------------------------------------------------
   New York  Oregon
a       7.0     8.0
c       9.0    10.0
e      11.0    12.0
f      16.0    17.0

   Ohio  Nevada  Missouri  Alabama  New York  Oregon
a   1.0     2.0       NaN      NaN       7.0     8.0
c   3.0     4.0       9.0     10.0       9.0    10.0
e   5.0     6.0      13.0     14.0      11.0    12.0

   Ohio  Nevada  Missouri  Alabama  New York  Oregon
a   1.0     2.0       NaN      NaN       7.0     8.0
c   3.0     4.0       9.0     10.0       9.0    10.0
e   5.0     6.0      13.0     14.0      11.0    12.0
b   NaN     NaN       7.0      8.0       NaN     NaN
d   NaN     NaN      11.0     12.0       NaN     NaN
f   NaN     NaN       NaN      NaN      16.0    17.0


2.3 沿轴向连接

另一种数据组合操作可以互换地称为拼接、绑定或堆叠。NumPy的concatenate函数可以在NumPy数组上实现该功能:

import numpy as np

arr = np.arange(12).reshape((3,4))
print(arr)
a = np.concatenate([arr,arr],axis=1)
print(a)
---------------------------------------------------------
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]
 
[[ 0  1  2  3  0  1  2  3]
 [ 4  5  6  7  4  5  6  7]
 [ 8  9 10 11  8  9 10 11]]

而在Seires和DataFrame等pandas对象的上下文中,使用标记的轴可以进一步泛化数组连接。尤其是你还有许多需要考虑的事情:

  1. 如果对象在其他轴上的索引不一样,我们是否应该将不同的元素组合在这些轴上,还是只使用共享的值?
  2. 连接的数据块是否需要在结果对象中被识别?
  3. ”连接轴“是否包含需要保存的数据?在许多情况下,DataFrame中的默认整数标签在连接期间最好丢弃。

pandas的concat函数提供了一种一致性的方式来解决以上问题。我将给出一些例子来表明它的工作机制。假设我们有三个索引不存在重叠的Series:

s1 = pd.Series([0,1],index=['a','b'])
s2 = pd.Series([2,3,4],index=['c','d','e'])
s3 = pd.Series([5,6],index=['f','g'])

a = pd.concat([s1,s2,s3])
print(a)
---------------------------------------------------------
a    0
b    1
c    2
d    3
e    4
f    5
g    6
dtype: int64

默认情况下,concat方法是沿着axis=0的轴向生效的,生成另一个Series。如果你传递axis=1,返回的结果则是一个DataFrame:

a = pd.concat([s1,s2,s3],axis=1)
print(a)
---------------------------------------------------------
     0    1    2
a  0.0  NaN  NaN
b  1.0  NaN  NaN
c  NaN  2.0  NaN
d  NaN  3.0  NaN
e  NaN  4.0  NaN
f  NaN  NaN  5.0
g  NaN  NaN  6.0

在这个案例中另一个轴向上并没有重叠,你可以看到排序后的索引合集是一个并集。你也可以传入join=‘inner’:

s4 = pd.concat([s1,s3])
print(s4)
a = pd.concat([s1,s4],axis=1)
print(a)
a = pd.concat([s1,s4],axis=1,join='inner')
print(a)
---------------------------------------------------------
a    0
b    1
f    5
g    6
dtype: int64

     0  1
a  0.0  0
b  1.0  1
f  NaN  5
g  NaN  6

   0  1
a  0  0
b  1  1

拼接在一起的各部分无法在结果中区分是一个潜在的问题,假设你想在连接轴向上创建一个多层索引,可以使用keys参数来实现:

result = pd.concat([s1,s1,s3],keys=['one','two','three'])
print(result)

a = result.unstack()
print(a)
---------------------------------------------------------
one    a    0
       b    1
two    a    0
       b    1
three  f    5
       g    6
dtype: int64

         a    b    f    g
one    0.0  1.0  NaN  NaN
two    0.0  1.0  NaN  NaN
three  NaN  NaN  5.0  6.0

沿着轴向axis=1连接Series的时候,keys则成为DataFrame的列头:

a = pd.concat([s1,s2,s3],axis=1,keys=['one','two','three'])
print(a)
---------------------------------------------------------
   one  two  three
a  0.0  NaN    NaN
b  1.0  NaN    NaN
c  NaN  2.0    NaN
d  NaN  3.0    NaN
e  NaN  4.0    NaN
f  NaN  NaN    5.0
g  NaN  NaN    6.0

将相同的逻辑扩展到DataFrame对象:

df1 = pd.DataFrame(np.arange(6).reshape(3, 2), index=['a', 'b', 'c'],
                   columns=['one', 'two'])
df2 = pd.DataFrame(5+np.arange(4).reshape(2,2),index=['a','c'],
                   columns=['three','four'])
print(df1)
print(df2)

a = pd.concat([df1,df2],axis=1,keys=['level1','level2'])
print(a)
---------------------------------------------------------
   one  two
a    0    1
b    2    3
c    4    5

   three  four
a      5     6
c      7     8

  level1     level2     
     one two  three four
a      0   1    5.0  6.0
b      2   3    NaN  NaN
c      4   5    7.0  8.0

如果你传递的是对象字典而不是列表的话,则字典的键会用于keys选项:

a = pd.concat({'level1':df1,'level2':df2},axis=1)
print(a)
---------------------------------------------------------
  level1     level2     
     one two  three four
a      0   1    5.0  6.0
b      2   3    NaN  NaN
c      4   5    7.0  8.0

我们还能使用names参数命名生成的轴层级:

a = pd.concat([df1,df2],axis=1,keys=['level1','level2'],names=['upper','lower'])
print(a)
---------------------------------------------------------
upper level1     level2     
lower    one two  three four
a          0   1    5.0  6.0
b          2   3    NaN  NaN
c          4   5    7.0  8.0

最后需要考虑的是行索引中不包含任何相关数据的DataFrame:

df11 = pd.DataFrame(np.random.rand(3,4),columns=['a','b','c','d'])
df22 = pd.DataFrame(np.random.rand(2,3),columns=['b','d','a'])
print(df11)
print(df22)
---------------------------------------------------------
          a         b         c         d
0  0.003492  0.525904  0.312933  0.633035
1  0.462266  0.738024  0.946863  0.276046
2  0.059198  0.818812  0.585546  0.776541

          b         d         a
0  0.181717  0.350863  0.587233
1  0.028107  0.294537  0.287630

当我们传入ignore_index= True时,会产生新的索引,而不是连接索引:

a = pd.concat([df11,df22],ignore_index=True)
print(a)
---------------------------------------------------------
          a         b         c         d
0  0.003492  0.525904  0.312933  0.633035
1  0.462266  0.738024  0.946863  0.276046
2  0.059198  0.818812  0.585546  0.776541
3  0.587233  0.181717       NaN  0.350863
4  0.287630  0.028107       NaN  0.294537

下表将介绍关于concat函数的参数:

参数描述
objs需要连接的pandas对象列表或字典;这是必选参数
axis连接的轴向;默认是0(沿着行方向)
join可以是’inner‘或’outer‘;用于指定连接方式是内连接还是外连接,默认是’outer‘
keys与要连接的对象关联的值,沿着连接轴形成分层索引;可以是任意值的列表或数组,也可以是数组的列表(如果向levels参数传入多层数组)
levels在键值传递时,该参数用于指定多层索引的层级
names如果传入了keys和或levels参数,该参数用于多层索引的层级名称
verify_integrity检查连接对象中的新轴是否重复,如果是,则引发异常;默认(False)允许重复
ignore_index不沿着连接轴保留索引,而产生一段新的索引(长度为total_length)

2.4 联合重叠数据

还有另一个数据联合场景,既不是合并操作,也不是连接操作。你可能有两个数据集,这两个数据集的索引全部或部分重叠。作为一个示例,考虑NumPy的where函数,这个函数可以进行面向数组的if-else等价操作:

a = pd.Series([np.nan,2.5,0.0,3.5,4.5,np.nan],
              index=['f','e','d','c','b','a'])

b = pd.Series([0.,np.nan,2.,np.nan,np.nan,5.],
              index=['a','b','c','d','e','f'])
print(a)
print(b)
a1 = np.where(pd.isnull(a),b,a)
print(a1)
---------------------------------------------------------
f    NaN
e    2.5
d    0.0
c    3.5
b    4.5
a    NaN
dtype: float64

a    0.0
b    NaN
c    2.0
d    NaN
e    NaN
f    5.0
dtype: float64
[0.  2.5 0.  3.5 4.5 5. ]

Series有一个combine_first方法,该方法可以等价于下面这种使用pandas常见数据对齐逻辑的轴向操作:

c = b.combine_first(a)
print(c)
---------------------------------------------------------
a    0.0
b    4.5
c    2.0
d    0.0
e    2.5
f    5.0
dtype: float64

在DataFrame中,combie_first逐列做相同的操作,因此你可以认为它是根据你传入的对象来”修补“调用对象的缺失值:

df1 = pd.DataFrame({'a':[1.,np.nan,5.,np.nan],
                    'b':[np.nan,2.,np.nan,6.],
                    'c':range(2,18,4)})

df2 = pd.DataFrame({'a':[5.,4.,np.nan,3.,7.],
                    'b':[np.nan,3.,4.,6.,8.]})
print(df1)
print(df2)
d = df1.combine_first(df2)
print(d)
---------------------------------------------------------
    a    b   c
0  1.0  NaN   2
1  NaN  2.0   6
2  5.0  NaN  10
3  NaN  6.0  14
     a    b
0  5.0  NaN
1  4.0  3.0
2  NaN  4.0
3  3.0  6.0
4  7.0  8.0
     a    b     c
0  1.0  NaN   2.0
1  4.0  2.0   6.0
2  5.0  4.0  10.0
3  3.0  6.0  14.0
4  7.0  8.0   NaN

总结

以上就是今天要讲的内容,本文简单介绍了分层索引的创建是以列表的列表作为索引,而我们学习到一个unstack方法可以对分层索引的数据在DataFrame进行重新排列,而unstack的反操作则是stack方法。而当你想改变层级排序是我们有swaplevel方法,对层级进行汇总统计时,level选项可以指定你想要在某个特定的轴上进行聚合。接下来就是包含pandas对象的数据进行联合与合并处理,这个时候我们就会用到merge方法,灵活调用该方法内部参数可以做到很多方式合并,DataFrame还有join方法以及concat来完成这些操作。而联合我们可以用到combine_first方法,就介绍到这些,下一篇即将介绍数据重塑和透视。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值