Pandas 中两个索引不同的 Dataframe 的运算问题

Pandas 最重要的一个功能是,它可以对不同索引的对象进行算术运算。在将对象相加时,结果的索引取索引对的并集。自动的数据对齐在不重叠的索引处引入空值,默认为 NaN。

In [1]:
import pandas as pd
In [2]:
date1 = pd.date_range('2012-3-1', periods=1)
data1 = {"A": 1, "B": 2, "C": 3}
df1 = pd.DataFrame(data=data1, index=date1)
In [3]:
df1
Out[3]:
A B C
2012-03-01 1 2 3
In [4]:
df1 + df1
Out[4]:
A B C
2012-03-01 2 4 6
In [5]:
df1 * df1
Out[5]:
A B C
2012-03-01 1 4 9
In [6]:
df1 - df1
Out[6]:
A B C
2012-03-01 0 0 0
In [7]:
df1 / df1
Out[7]:
A B C
2012-03-01 1 1 1

如果两个对象的索引或者列不同,pandas 会自动进行数据对齐,不对齐的数据会默认用 NaN 填充。

In [8]:
date2 = pd.date_range('2012-4-1', periods=1)
data2 = {"A": 4, "B": 5, "C": 6}
df2 = pd.DataFrame(data=data2, index=date2)
In [9]:
df2
Out[9]:
A B C
2012-04-01 4 5 6
In [10]:
df1 + df2
Out[10]:
A B C
2012-03-01 NaN NaN NaN
2012-04-01 NaN NaN NaN

这可能不是你希望的结果,你只是想将两个 dataframe 合并在一起,那么可以采用 dataframe 对象的 add() 方法来相加,并指定 fill_value 参数:

In [11]:
df1.add(df2,  fill_value=0)
Out[11]:
A B C
2012-03-01 1 2 3
2012-04-01 4 5 6

如果你仅仅只是需要拼接两个 dataframe,那么直接使用 append 方法即可

In [12]:
df1.append(df2)
Out[12]:
A B C
2012-03-01 1 2 3
2012-04-01 4 5 6

也许这也不是某些人想要的结果,他们希望两个 dataframe 对应的行相加,即使索引不同。这就违背了 pandas 数据对齐的特性,这也是 dataframe 行索引和列列索引存在的意义。所以 dataframe 原生不支持这样的运算,为什么是这样呢?想想如果让两个索引不同的 dataframe 的对应列相加,那么加完之后新的对象的索引该用谁的呢。当然,如果非要这么做也并不是不可,我们可以用一些迂回的办法:

  • 1、转化成 series 后进行运算:
In [13]:
pd.DataFrame(df1.values + df2.values, columns=df1.columns)
Out[13]:
A B C
0 5 7 9
  • 2、改变其中一个 dataframe 的行索引,使两个 dataframe 的行索引相同:
In [14]:
df2_bak = df2.copy()  # 如果不希望改变原对象的索引就做一份拷贝
df2_bak.index = df1.index
In [15]:
df2_bak
Out[15]:
A B C
2012-03-01 4 5 6
In [16]:
df1 + df2_bak
Out[16]:
A B C
2012-03-01 5 7 9
  • 3、用 reset_index() 方法重置列索引, 该方法返回一个新的 dataframe:
In [17]:
df1.reset_index()
Out[17]:
index A B C
0 2012-03-01 1 2 3
In [18]:
df2.reset_index()
Out[18]:
index A B C
0 2012-04-01 4 5 6

直接 reset 时,我们发现原来的 index 默认被作为 dataframe 的一列,这可能不是我们想要的。我们可以给 reset_index() 方法传递一个参数 drop=True

In [19]:
df1.reset_index(drop=True)
Out[19]:
A B C
0 1 2 3
In [20]:
df2.reset_index(drop=True)
Out[20]:
A B C
0 4 5 6
In [21]:
df1.reset_index(drop=True) + df2.reset_index(drop=True)
Out[21]:
A B C
0 5 7 9

如果仅仅是列索引不同,也可以用同样的方法:

In [22]:
data3 = {"D": 7, "E": 8, "F": 9}
df3 = pd.DataFrame(data=data3, index=date1)
In [23]:
df3
Out[23]:
D E F
2012-03-01 7 8 9
In [24]:
df1 + df3
Out[24]:
A B C D E F
2012-03-01 NaN NaN NaN NaN NaN NaN
In [25]:
pd.DataFrame(df1.values + df2.values, columns=df3.columns)
Out[25]:
D E F
0 5 7 9

那么到底什么是数据对齐,就是两个 dataframe 的相应位置没有一一对应的数据。

In [26]:
data4 = {"D": 7, "E": 8, "F": 9, "G": 10}
df4 = pd.DataFrame(data=data4, index=date1)
In [27]:
df4
Out[27]:
D E F G
2012-03-01 7 8 9 10
In [28]:
df3 + df4
Out[28]:
D E F G
2012-03-01 14 16 18 NaN

这里在相加时,df3 实际上是变成了下面的样子(也就是在没有数据的位置默认用 NaN 填充):

In [29]:
df3 = pd.DataFrame(data=data3, index=date1, columns=list("DEFG"))
In [30]:
df3
Out[30]:
D E F G
2012-03-01 7 8 9 NaN
In [31]:
df3
Out[31]:
D E F G
2012-03-01 7 8 9 NaN

之所两个 df3 + df4 的 ‘G’ 所以对应的值为 NaN, 是因为任何数与 NaN 做运算都等于 NaN。

In [32]:
1 + np.nan
Out[32]:
nan

可以用 fillna 方法来重置填充值:

In [33]:
df3.fillna(0)
Out[33]:
D E F G
2012-03-01 7 8 9 0
In [34]:
df3.fillna(0) + df4
Out[34]:
D E F G
2012-03-01 14 16 18 10
In [35]:
df1
Out[35]:
A B C
2012-03-01 1 2 3
In [36]:
df1.reset_index(drop=True)
Out[36]:
A B C
0 1 2 3
In [37]:
df1
Out[37]:
A B C
2012-03-01 1 2 3

by huoty
2016-07-07