pandas

算数运算和数据对齐

pandas中的一个功能是对不同索引的对象进行算数运算。在运算时,如果存在不同的索引对,那么结果就是它们的并集。

In [122]: s1 = Series([7.3, -2.5, 3.4, 1.5], index=['a','c','d','e'])

In [123]: s2 = Series([-2.1, 3.6, -1.5, 4, 3.1], index=['a','c','e','f','g'])

In [124]: s1
Out[124]:
a    7.3
c   -2.5
d    3.4
e    1.5
dtype: float64

In [125]: s2
Out[125]:
a   -2.1
c    3.6
e   -1.5
f    4.0
g    3.1
dtype: float64

In [126]: s1 + s2
Out[126]:
a    5.2
c    1.1
d    NaN
e    0.0
f    NaN
g    NaN
dtype: float64

可以看到,不重叠的索引处因为数据对齐功能的缘故被引入了NaN值。相当于s1中的f、g索引和s2中的d索引本身都为缺失值NaN,由于n + NaN = NaN的特性,所以才有了这样的结果。

同样在DataFrame上也会是这种情况:

In [127]: df1 = DataFrame(np.arange(9.).reshape((3, 3)), columns=list('bcd'),index=['Ohio','Texas','Colorado'])
In [129]: df2 = DataFrame(np.arange(12.).reshape(4, 3), columns=list('bde'), index=['Utah','Ohio','Texas','Oregon'])

In [130]: df1
Out[130]:
            b    c    d
Ohio      0.0  1.0  2.0
Texas     3.0  4.0  5.0
Colorado  6.0  7.0  8.0

In [131]: df2
Out[131]:
          b     d     e
Utah    0.0   1.0   2.0
Ohio    3.0   4.0   5.0
Texas   6.0   7.0   8.0
Oregon  9.0  10.0  11.0

In [132]: df1 + df2
Out[132]:
            b   c     d   e
Colorado  NaN NaN   NaN NaN
Ohio      3.0 NaN   6.0 NaN
Oregon    NaN NaN   NaN NaN
Texas     9.0 NaN  12.0 NaN
Utah      NaN NaN   NaN NaN

如何解决这个问题?再看下面的例子:

在算数方法中填充值

pandas对运算设计了几个函数,通过这个函数我们可以预先设置好填充值来解决这个问题。

In [134]: df1.add(df2, fill_value=0)
Out[134]:
            b    c     d     e
Colorado  6.0  7.0   8.0   NaN
Ohio      3.0  1.0   6.0   5.0
Oregon    9.0  NaN  10.0  11.0
Texas     9.0  4.0  12.0   8.0
Utah      0.0  NaN   1.0   2.0

add 加法
sub 减法
div 除法
mul 乘法

DataFrame和Series之间的运算

计算二维数组和某行之差

In [135]: arr = np.arange(12).reshape((3,4))

In [136]: arr
Out[136]:
array([[ 0,  1,  2,  3],
       [ 4,  5,  6,  7],
       [ 8,  9, 10, 11]])

In [137]: arr[0]
Out[137]: array([0, 1, 2, 3])

In [138]: arr - arr[0]
Out[138]:
array([[0, 0, 0, 0],
       [4, 4, 4, 4],
       [8, 8, 8, 8]])

数组中的每一行都对该数组进行了运算,这个过程叫做广播。
再来看看DataFrameSeries之间的运算:

In [139]: frame = DataFrame(np.arange(12).reshape((4,3)), columns=list('bde'),index=['Utah','Ohio','Texas','Oregon'])

In [140]: series = frame.iloc[0]

In [141]: frame
Out[141]:
        b   d   e
Utah    0   1   2
Ohio    3   4   5
Texas   6   7   8
Oregon  9  10  11

In [142]: series
Out[142]:
b    0
d    1
e    2
Name: Utah, dtype: int64

In [143]: frame - series
Out[143]:
        b  d  e
Utah    0  0  0
Ohio    3  3  3
Texas   6  6  6
Oregon  9  9  9

DataFrameSeries的运算中匹配到了Utah这行然后沿着行向下广播得到上面的答案。

如果想在列上广播,那首先想到的肯定是修改axis选项啦

In [144]: series3 = frame['d']

In [145]: series3
Out[145]:
Utah       1
Ohio       4
Texas      7
Oregon    10
Name: d, dtype: int64

In [146]: frame.sub(series3, axis = 0)
Out[146]:
        b  d  e
Utah   -1  0  1
Ohio   -1  0  1
Texas  -1  0  1
Oregon -1  0  1

在这里我的理解是series3匹配到了列,沿着列向右广播过去。

函数应用和映射

NumPy的很多方法也可用于操作pandas对象:

In [150]: frame = DataFrame(np.random.randn(4, 3), columns=list('bde'), index=['Utah','Ohio','Texas','Oregon'])

In [151]: frame
Out[151]:
               b         d         e
Utah   -0.708756  0.996288 -2.136221
Ohio   -0.615091 -1.440153 -0.390829
Texas   0.467176 -0.000329 -3.147984
Oregon -0.107895  1.029447 -0.309974

In [152]: np.abs(frame)
Out[152]:
               b         d         e
Utah    0.708756  0.996288  2.136221
Ohio    0.615091  1.440153  0.390829
Texas   0.467176  0.000329  3.147984
Oregon  0.107895  1.029447  0.309974

还有一种比较好玩的方法是,将函数应用到DataFrame上。我们可以通过DataFrameapply函数实现此功能:

In [153]: f = lambda x: x.max() - x.min()

In [154]: frame.apply(f)
Out[154]:
b    1.175931
d    2.469600
e    2.838010
dtype: float64

axis 默认为0,以列为目标执行f函数。
当然反过来也是可以的:

In [155]: frame.apply(f, axis=1)
Out[155]:
Utah      3.132508
Ohio      1.049324
Texas     3.615160
Oregon    1.339421
dtype: float64

以行为目标执行f函数。

元素级的Python函数也是可以用的,也就是将函数作用到每一个元素:

In [156]: format = lambda x: '%.2f' % x

In [157]: frame.applymap(format)
Out[157]:
            b      d      e
Utah    -0.71   1.00  -2.14
Ohio    -0.62  -1.44  -0.39
Texas    0.47  -0.00  -3.15
Oregon  -0.11   1.03  -0.31

frame中的数值转换成了浮点数。

相较于apply其实有更简便的方式map, map函数更针对于单个元素(某列或者某行):

In [158]: frame['e'].map(format)
Out[158]:
Utah      -2.14
Ohio      -0.39
Texas     -3.15
Oregon    -0.31
Name: e, dtype: object

接下来会详细出一篇用来区分mapapplyapplymap之间的用法与区别的文章。

排序和排名

对行或列索引排序

  • 对列进行排序从而将行索引排成了升序
In [169]: frame = DataFrame(np.arange(8).reshape((2, 4)), index=['three', 'one'], columns=['d','a','b','c'])

In [171]: frame.sort_index()
Out[171]:
       d  a  b  c
one    4  5  6  7
three  0  1  2  3
  • 对行进行排序从而将列索引排成了升序*
In [170]: frame.sort_index(axis=1)
Out[170]:
       a  b  c  d
three  1  2  3  0
one    5  6  7  4

也可以降序排序

In [173]: frame.sort_index(axis=1, ascending=False)
Out[173]:
       d  c  b  a
three  0  3  2  1
one    4  7  6  5

如果是想对数据进行排序

In [174]: obj = Series([4, 7, -3, 2])
In [176]: obj.sort_values()
Out[176]:
2   -3
3    2
0    4
1    7
dtype: int64

数据中如果有缺失值,在排序后会强制放到末尾

In [177]: obj = Series([4, np.nan, 7, np.nan, -3, 2])

In [178]: obj.sort_values()
Out[178]:
4   -3.0
5    2.0
0    4.0
2    7.0
1    NaN
3    NaN
dtype: float64

在DataFrame中,可以对一个或者多个列中的值进行排序

In [179]: frame = DataFrame({'b':[4, 7, -3, 2], 'a':[0, 1, 0, 1]})
In [181]: frame.sort_values(by='b')
Out[181]:
   b  a
2 -3  0
3  2  1
0  4  0
1  7  1

对多个列进行排序的话,优先还是按照数组中第一项来排,也就是下面这个例子中的b

In [182]: frame.sort_values(by=['b','a'])
Out[182]:
   b  a
2 -3  0
3  2  1
0  4  0
1  7  1
排名

排名和排序不同,排名会在不动索引的情况下对当前数据做一个排名。如果出现平级的情况就会分配一个平均排名。

In [183]: obj = Series([7, -5, 7, 4, 2, 0, 4])

In [184]: obj.rank()
Out[184]:
0    6.5
1    1.0
2    6.5
3    4.5
4    3.0
5    2.0
6    4.5
dtype: float64

也可以对排序完的数据按照顺序进行排名

In [186]: obj.rank(method='first')
Out[186]:
0    6.0
1    1.0
2    7.0
3    4.0
4    3.0
5    2.0
6    5.0
dtype: float64

一开始可能看不懂,其实这里应该要这么理解:

In [185]: obj.sort_values()
Out[185]:
1   -5   #1
5    0   #2
4    2   #3
3    4   #4
6    4   #5
0    7   #6
2    7   #7
dtype: int64

下表列出了常用的method选项
average 默认: 在相等分组中,为各个值分配平均排名
min: 按从小到大进行排名
max: 按从大到小进行排名
first: 按默认排序后的出现顺序进行排名