【利用python进行数据分析】第5章pandas入门

pandas的数据结构介绍

要使用pandas,你首先就得熟悉它的两个主要数据结构:Series和DataFrame。虽然它们并不能解决所有的问题,但它们为大多数应用提供了一种可靠的、易于使用的基础。由于频繁的使用pandas、Series和DataFrame,所以我们将其引入本地命名空间中会更方便

from pandas import Series, DataFrame
import pandas as pd

Series

Series是一种类似于一维数组的对象,它由一组数据(各种NumPy数据类型)以及一组与之相关的数据标签(即索引)组成的。仅由一组数据即可产生最简单的Series:

obj=Series([4,7,-5,3])
print(obj)
print(obj.values)
print(obj.index)

Series的字符串表现形式为:索引在左边,值在右边。由于我们没有为数据指定索引,于是会自动创建一个0到N-1(N为数据的长度)的整数型索引。你可以通过Series的values和index属性获取其数组表示形式和索引对象,输出结果如下:

0    4
1    7
2   -5
3    3
dtype: int64
[ 4  7 -5  3]
RangeIndex(start=0, stop=4, step=1)

Series与普通Numpy数组相比,可以通过索引的方式选取Series中的单个或一组值:

obj2=Series([4,7,-5,3],index=['d','b','a','c'])
print(obj2)
print(obj2['b'])
print(obj2[['a','b','c']])

输出结果:

d    4
b    7
a   -5
c    3
dtype: int64
7
a   -5
b    7
c    3
dtype: int64

Numpy数组运算(如根据布尔型数组进行过滤、标量乘法、应用数学函数等)都会保留索引和值之间的链接:

obj2=Series([4,7,-5,3],index=['d','b','a','c'])
print(obj2)
print(obj2[obj2>0])
print(obj2*2)
print(np.exp(obj2))
#in检索Series的index
print('b' in obj2)

输出结果:

d    4
b    7
a   -5
c    3
dtype: int64
d    4
b    7
c    3
dtype: int64
d     8
b    14
a   -10
c     6
dtype: int64
d      54.598150
b    1096.633158
a       0.006738
c      20.085537
dtype: float64
True

通过字典创建Series,如果只传入一个字典,则结果Series中的索引就是原字典的键(有序排列)

sdata = {'Ohio': 35000, 'Texas': 71000, 'Oregon': 16000, 'Utah': 5000}
obj3=Series(sdata)
print(obj3)
states = ['California', 'Ohio', 'Oregon', 'Texas']
obj4=Series(sdata,index=states)
print(obj4)

输出结果

Ohio      35000
Oregon    16000
Texas     71000
Utah       5000
dtype: int64
California        NaN
Ohio          35000.0
Oregon        16000.0
Texas         71000.0
dtype: float64

pandas的isnull和notnull函数可用于检测上述缺失数据,Series也有类似的实例方法:

print(pd.isnull(obj4))
print(pd.notnull(obj4))
print(obj4.isnull())

输出结果:

California     True
Ohio          False
Oregon        False
Texas         False
dtype: bool
California    False
Ohio           True
Oregon         True
Texas          True
dtype: bool
California     True
Ohio          False
Oregon        False
Texas         False
dtype: bool

Series最重要的一个功能是:它在算术运算中会自动对齐不同索引的数据

print(obj3)
print(obj4)
print(obj3+obj4)

输出结果:

Ohio      35000
Oregon    16000
Texas     71000
Utah       5000
dtype: int64
California        NaN
Ohio          35000.0
Oregon        16000.0
Texas         71000.0
dtype: float64
California         NaN
Ohio           70000.0
Oregon         32000.0
Texas         142000.0
Utah               NaN        #索引有值做加法,没值做并集
dtype: float64

Series对象本身及其索引都有一个name属性,该属性跟pandas其他的关键功能关系非常密切:

print(obj4)
obj4.name = 'population'
obj4.index.name='state'
print(obj4)

输出结果:

California        NaN
Ohio          35000.0
Oregon        16000.0
Texas         71000.0
dtype: float64
state
California        NaN
Ohio          35000.0
Oregon        16000.0
Texas         71000.0
Name: population, dtype: float64

Series的索引可以通过赋值的方式就地修改:

print(obj)
obj.index= ['Bob', 'Steve', 'Jeff', 'Ryan']
print(obj)

输出结果:

0    4
1    7
2   -5
3    3
dtype: int64
Bob      4
Steve    7
Jeff    -5
Ryan     3
dtype: int64

DataFrame

DataFrame是一个表格型的数据结构,它含有一组有序的列,每列可以是不同的值类型(数值、字符串、布尔值等)。DataFrame既有行索引也有列索引,它可以被看做由Series组成的字典(共用同一个索引)。跟其他类似的数据结构相比(如R的data.frame),DataFrame中面向行和面向列的操作基本上是平衡的。其实,DataFrame中的数据是以一个或多个二维块存放的(而不是列表、字典或别的一堆数据结构)。

构建DataFrame的办法有很多,最常用的一种是直接传入一个由等长列表或Numpy数组组成的字典:

data = {'state': ['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada'], 'year': [2000, 2001, 2002, 2001, 2002], 'pop': [1.5, 1.7, 3.6, 2.4, 2.9]}
frame = DataFrame(data)
print(frame)
#按指定顺序排列
print(DataFrame(data, columns=['year', 'state', 'pop']))

输出结果:

   pop   state  year
0  1.5    Ohio  2000
1  1.7    Ohio  2001
2  3.6    Ohio  2002
3  2.4  Nevada  2001
4  2.9  Nevada  2002
   year   state  pop
0  2000    Ohio  1.5
1  2001    Ohio  1.7
2  2002    Ohio  3.6
3  2001  Nevada  2.4
4  2002  Nevada  2.9

跟Series一样,如果传入的列在数据中找不到,就会产生NA值:

data = {'state': ['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada'], 'year': [2000, 2001, 2002, 2001, 2002], 'pop': [1.5, 1.7, 3.6, 2.4, 2.9]}
frame2 = DataFrame(data, columns=['year', 'state', 'pop', 'debt'],
                   index=['one', 'two', 'three', 'four', 'five'])
print(frame2)
#通过类似字典标记的方式或者属性的方式,可以将DataFrame的列获取为一个Series
print(frame2['state'])
print(frame2.state)
#行也可以通过位置或名称的方式进行获取,比如用索引字段ix
print(frame2.ix['three'])

输出结果:

       year   state  pop debt
one    2000    Ohio  1.5  NaN
two    2001    Ohio  1.7  NaN
three  2002    Ohio  3.6  NaN
four   2001  Nevada  2.4  NaN
five   2002  Nevada  2.9  NaN
one        Ohio
two        Ohio
three      Ohio
four     Nevada
five     Nevada
Name: state, dtype: object
one        Ohio
two        Ohio
three      Ohio
four     Nevada
five     Nevada
Name: state, dtype: object
year     2002
state    Ohio
pop       3.6
debt      NaN
Name: three, dtype: object

列可以通过赋值的方式进行修改,将列表或数组赋值给某个列时,其长度必须跟DataFrame的长度相匹配,如果赋值的是一个Series,就会精确匹配DataFrame的索引。为不存在的列赋值会创建一个新列。关键字del用于删除列。

frame2['debt']=16.5
print(frame2)
frame2['debt']=np.arange(5)
print(frame2)
val = Series([-1.2, -1.5, -1.7], index=['two', 'four', 'five'])
frame2['debt'] = val
print(frame2)
frame2['eastern'] = frame2.state == 'Ohio'
print(frame2)
#警告:Series所做的任何就地修改全部都会反映到DataFrame上
del frame2['eastern']
print(frame2)

输出结果:

       year   state  pop  debt
one    2000    Ohio  1.5  16.5
two    2001    Ohio  1.7  16.5
three  2002    Ohio  3.6  16.5
four   2001  Nevada  2.4  16.5
five   2002  Nevada  2.9  16.5
       year   state  pop  debt
one    2000    Ohio  1.5     0
two    2001    Ohio  1.7     1
three  2002    Ohio  3.6     2
four   2001  Nevada  2.4     3
five   2002  Nevada  2.9     4
       year   state  pop  debt
one    2000    Ohio  1.5   NaN
two    2001    Ohio  1.7  -1.2
three  2002    Ohio  3.6   NaN
four   2001  Nevada  2.4  -1.5
five   2002  Nevada  2.9  -1.7
       year   state  pop  debt eastern
one    2000    Ohio  1.5   NaN    True
two    2001    Ohio  1.7  -1.2    True
three  2002    Ohio  3.6   NaN    True
four   2001  Nevada  2.4  -1.5   False
five   2002  Nevada  2.9  -1.7   False
       year   state  pop  debt
one    2000    Ohio  1.5   NaN
two    2001    Ohio  1.7  -1.2
three  2002    Ohio  3.6   NaN
four   2001  Nevada  2.4  -1.5
five   2002  Nevada  2.9  -1.7

另一种常见的数据形式是嵌套字典,外层字典的键作为列,内层键则作为行索引

pop = {'Nevada': {2001: 2.4, 2002: 2.9},'Ohio': {2000: 1.5, 2001: 1.7, 2002: 3.6}}
frame3 = DataFrame(pop)
print(frame3)
print(frame3.T)
print(DataFrame(pop, index=[2001, 2002, 2003]))
#可以设置DataFrame的index和columns的name属性
frame3.index.name='year'
frame3.columns.name='state'
print(frame3)
#DataFrame的values属性会以二维ndarray的形式返回DataFrame中的数据
print(frame3.values)

输出结果:

      Nevada  Ohio
2000     NaN   1.5
2001     2.4   1.7
2002     2.9   3.6
        2000  2001  2002
Nevada   NaN   2.4   2.9
Ohio     1.5   1.7   3.6
      Nevada  Ohio
2001     2.4   1.7
2002     2.9   3.6
2003     NaN   NaN
state  Nevada  Ohio
year               
2000      NaN   1.5
2001      2.4   1.7
2002      2.9   3.6
[[ nan  1.5]
 [ 2.4  1.7]
 [ 2.9  3.6]]

pandas的索引对象负责管理轴标签和其他元数据(比如轴名称等)。Index对象是不可修改的(immutable),因此用户不能对其进行修改,不可修改性非常重要,因为这样才能使Index对象在多个数据结构之间安全共享,这里就不举例了。

基本功能

调用Series的reindex方法将会根据新索引进行重排,如果某个索引值当前不存在,就引入缺失值或者用关键字参数fill_value指定

obj = Series([4.5, 7.2, -5.3, 3.6], index=['d', 'b', 'a', 'c'])
obj2 = obj.reindex(['a', 'b', 'c', 'd', 'e'])
print(obj2)
obj2=obj.reindex(['a', 'b', 'c', 'd', 'e'], fill_value=0)
print(obj2)

输出结果:

a   -5.3
b    7.2
c    3.6
d    4.5
e    NaN
dtype: float64
a   -5.3
b    7.2
c    3.6
d    4.5
e    0.0
dtype: float64

对于时间序列这样的有序数据,重新索引时可能需要做一些插值处理,关键字参数method选项即可达到此目的。例如,使用ffill可以实现前向值填充,使用bfill实现后向填充:

obj3 = Series(['blue', 'purple', 'yellow'], index=[0, 2, 4])
print(obj3)
print(obj3.reindex(range(6), method='ffill'))
print(obj3.reindex(range(6), method='bfill'))

输出结果:

0      blue
2    purple
4    yellow
dtype: object
0      blue
1      blue
2    purple
3    purple
4    yellow
5    yellow
dtype: object
0      blue
1    purple
2    purple
3    yellow
4    yellow
5       NaN
dtype: object

丢弃指定轴上的项。丢弃某条轴上的一个或多个项很简单,只要一个索引数组或列表即可,drop方法返回的是一个在指定轴上删除了指定值的新对象。一般drop都是新生成一个对象,若使用inplace=true,则会在原对象上修改:

#Series丢弃指定轴上的项
obj = Series(np.arange(5.), index=['a', 'b', 'c', 'd', 'e'])
print(obj.drop('c'))
print(obj.drop(['d', 'c']))
#DataFrame丢弃指定轴上的项
data = DataFrame(np.arange(16).reshape((4, 4)),index=['Ohio', 'Colorado', 'Utah', 'New York'],
                 columns=['one', 'two', 'three', 'four'])
print(data.drop(['Colorado', 'Ohio']))
print(data.drop('two', axis=1))
print(data.drop(['two', 'four'], axis=1))

输出结果:

a    0.0
b    1.0
d    3.0
e    4.0
dtype: float64
a    0.0
b    1.0
e    4.0
dtype: float64
          one  two  three  four
Utah        8    9     10    11
New York   12   13     14    15
          one  three  four
Ohio        0      2     3
Colorado    4      6     7
Utah        8     10    11
New York   12     14    15
          one  three
Ohio        0      2
Colorado    4      6
Utah        8     10
New York   12     14

索引、选取和过滤

# Series索引(obj[...])的工作方式类似于NumPy数组的索引,只不过Series的索引值不只是整数
obj = Series(np.arange(4.), index=['a', 'b', 'c', 'd'])
print(obj['b'])  #obj[1]
print(obj[2:4])
print(obj[['b', 'a', 'd']])
print(obj[[1, 3]])
print(obj[obj < 2])
#如你所见,对DataFrame进行索引其实就是获取一个或多个列:
data = DataFrame(np.arange(16).reshape((4, 4)),index=['Ohio', 'Colorado', 'Utah', 'New York'],
                 columns=['one', 'two', 'three', 'four'])
print(data['two'])
print(data[['three', 'one']])
print(data[:2])
print(data[data['three'] > 5])
data[data < 5]=0
print(data)
print(data.ix['Colorado', ['two', 'three']])
print(data.ix[['Colorado', 'Utah'], [3, 0, 1]])
print(data.ix[data.three > 5, :3])

输出结果:

1.0
c    2.0
d    3.0
dtype: float64
b    1.0
a    0.0
d    3.0
dtype: float64
b    1.0
d    3.0
dtype: float64
a    0.0
b    1.0
dtype: float64
Ohio         1
Colorado     5
Utah         9
New York    13
Name: two, dtype: int32
          three  one
Ohio          2    0
Colorado      6    4
Utah         10    8
New York     14   12
          one  two  three  four
Ohio        0    1      2     3
Colorado    4    5      6     7
          one  two  three  four
Colorado    4    5      6     7
Utah        8    9     10    11
New York   12   13     14    15
          one  two  three  four
Ohio        0    0      0     0
Colorado    0    5      6     7
Utah        8    9     10    11
New York   12   13     14    15
two      5
three    6
Name: Colorado, dtype: int32
          four  one  two
Colorado     7    0    5
Utah        11    8    9
          one  two  three
Colorado    0    5      6
Utah        8    9     10
New York   12   13     14

算术运算和数据对齐。pandas最重要的一个功能是它可以对不同索引的对象进行算术运算。在将Series对象相加时,如果存在不同的索引对,则结果的索引就是该索引对的并集。自动的数据对齐操作在不重叠的索引处引入了NaN值。对于DataFrame,对齐操作会同时发生在行和列上。

算术运算方法有add、sub、div、mul。DataFrame和Series之间的运算。

在对不同索引的对象进行算术运算时,你可能希望当一个对象中某个轴标签在另一个对象中找不到时填充一个特殊值(比如0):

df1 = DataFrame(np.arange(12.).reshape((3, 4)), columns=list('abcd'))
df2 = DataFrame(np.arange(20.).reshape((4, 5)), columns=list('abcde'))
print(df1.add(df2, fill_value=0))

输出结果:

      a     b     c     d     e
0   0.0   2.0   4.0   6.0   4.0
1   9.0  11.0  13.0  15.0   9.0
2  18.0  20.0  22.0  24.0  14.0
3  15.0  16.0  17.0  18.0  19.0

DataFrame和Series之间的运算。来看一个启发性的例子:

frame = DataFrame(np.arange(12.).reshape((4, 3)), columns=list('bde'),index=['Utah', 'Ohio', 'Texas', 'Oregon'])
print(frame - frame.ix[0])

输出结果:

          b    d    e
Utah    0.0  0.0  0.0
Ohio    3.0  3.0  3.0
Texas   6.0  6.0  6.0
Oregon  9.0  9.0  9.0

这就叫广播,默认情况下,DataFrame和Series之间的算术运算会将Series的索引匹配到DataFrame的列,然后沿着行一直向下广播。

如果希望在列上广播:

frame.sub(frame['d'], axis=0)

特别注意:轴用来为超过一维的数组定义的属性,二维数据拥有两个轴:第0轴沿着行的垂直往下,第1轴沿着列的方向水平延伸。如果简单点来说,就是0轴匹配的是index, 涉及上下运算;1轴匹配的是columns, 涉及左右运算。

函数应用和映射

这在数据清洗的时候是用的最多的,所以这是我把标题加粗的原因。NumPy的ufuncs(元素级数组方法)也可用于操作pandas对象:

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

输出结果:

               b         d         e
Utah   -0.593021 -0.586625  0.248056
Ohio   -1.181193 -0.731296  0.032435
Texas   2.097144  0.120056  0.553435
Oregon -1.291559  0.534635  0.260379
               b         d         e
Utah    0.593021  0.586625  0.248056
Ohio    1.181193  0.731296  0.032435
Texas   2.097144  0.120056  0.553435
Oregon  1.291559  0.534635  0.260379

另一个常见的操作是,将函数应用到由各列或行所形成的一维数组上。DataFrame的apply方法即可实现此功能:

f = lambda x: x.max() - x.min()
print(frame.apply(f))
print(frame.apply(f,axis=1))

输出结果:

b    3.388703
d    1.265931
e    0.521000
dtype: float64
Utah      0.841077
Ohio      1.213628
Texas     1.977089
Oregon    1.826194
dtype: float64

许多最为常见的数组统计功能都被实现成DataFrame的方法(如sum和mean),因此无需使用apply方法。

除标量值外,传递给apply的函数还可以返回由多个值组成的Series:

def f(x):
    return Series([x.min(), x.max()], index=['min', 'max'])
print(frame.apply(f))

输出结果:

            b         d         e
min -1.268524 -0.262641 -1.020867
max  0.388243  2.310811  1.461124

此外,元素级的Python函数也是可以用的。假如你想得到frame中各个浮点值的格式化字符串,使用applymap即可:

format=lambda x:'%.2f' %x
print(frame.applymap(format))

输出结果:

            b      d      e
Utah    -0.13  -0.43  -0.29
Ohio     1.11   0.47  -0.68
Texas   -1.14  -1.68   0.14
Oregon   0.27   2.15  -0.84

之所以叫做applymap,是因为Series有一个用于应用元素级函数的map方法:

frame['e'].map(format)

排序和排名

根据条件对数据集排序(sorting)也是一种重要的内置运算。

obj=Series(range(4),index=['d','a','b','c'])
print(obj.sort_index())
frame = DataFrame(np.arange(8).reshape((2, 4)), index=['three', 'one'],columns=['d', 'a', 'b', 'c'])
print(frame.sort_index(axis=1,ascending=False))
obj=Series([4,7,-3,2])
print(obj.sort_values())
#任何缺失值都会被放到Series的末尾
obj=Series([4,np.nan,7,np.nan,-3,2])
print(obj.sort_values())

输出结果:

a    1
b    2
c    3
d    0
dtype: int32
       d  c  b  a
three  0  3  2  1
one    4  7  6  5
2   -3
3    2
0    4
1    7
dtype: int64
4   -3.0
5    2.0
0    4.0
2    7.0
1    NaN
3    NaN
dtype: float64

在DataFrame上,你可能希望根据一个或多个列中的值进行排序。将一个或多个列的名字传递给by选项即可达到该目的:

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

输出结果:

   a  b
2  0 -3
0  0  4
3  1  2
1  1  7

排名(ranking)跟排序关系密切,且它会增设一个排名值(从1开始,一直到数组中有效数据的数量)。它跟numpy.argsort产生的间接排序索引差不多,只不过它可以根据某种规则破坏平级关系。接下来介绍Series和DataFrame的rank方法。默认情况下,rank是通过“为各组分配一个平均排名”的方式破坏平级关系的:

obj = Series([7, -5, 7, 4, 2, 0, 4])
print(obj.rank())
print(obj.rank(method='first'))

输出结果:

0    6.5
1    1.0
2    6.5
3    4.5
4    3.0
5    2.0
6    4.5
dtype: float64
0    6.0
1    1.0
2    7.0
3    4.0
4    3.0
5    2.0
6    5.0
dtype: float64

DataFrame可以在行或列上计算排名:

frame = DataFrame({'b': [4.3, 7, -3, 2], 'a': [0, 1, 0, 1],'c': [-2, 5, 8, -2.5]})
print(frame.rank(axis=1))

输出结果:

     a    b    c
0  2.0  3.0  1.0
1  1.0  3.0  2.0
2  2.0  1.0  3.0
3  2.0  3.0  1.0

汇总和计算描述统计

举个例子:

df = DataFrame([[1.4, np.nan], [7.1, -4.5],[np.nan, np.nan], [0.75, -1.3]],
               index=['a', 'b', 'c', 'd'],columns=['one', 'two'])
print(df.idxmax())

输出结果:

one    b
two    d
dtype: object

描述和汇总统计

方法 说明
count 非NA值的数量
describe 针对Series或各DataFrame列计算汇总统计
min、max 计算最小值和最大值
argmin、argmax 计算能够获取到最小值和最大值的索引位置(整数)
idxmin、idxmax 计算能够获取到最小值和最大值的索引值
quantile 计算样本的分位数(0到1)
sum 值的总和
mean 值的平均数
median 值的算术中位数(50%分位数)
mad 根据平均值计算平均绝对离差
var 样本值的方差
std 样本值的标准差
skew 样本值的偏度(三阶矩)
kurt 样本值的峰度(四阶矩)
cumsum 样本值的累计和
cummin、cummax 样本值的累计最大值和累计最小值
cumprod 样本值的累计积
diff 计算一阶差分(对时间序列很有用)
pct_change 计算百分数变化

唯一值、值计数以及成员资格

unique()用于计算Series唯一值数组,value_counts()用于计算Series中各值出现的频率,isin()用于判断矢量化集合的成员资格返回布尔值。

有时,你可能希望得到DataFrame中多个相关列的一张柱状图。例如:

data = DataFrame({'Qu1': [1, 3, 4, 3, 4],'Qu2': [2, 3, 1, 2, 3],'Qu3': [1, 5, 2, 4, 4]})
result = data.apply(pd.value_counts).fillna(0)
print(result)

输出结果:

   Qu1  Qu2  Qu3
1  1.0  1.0  1.0
2  0.0  2.0  1.0
3  2.0  2.0  0.0
4  2.0  0.0  2.0
5  0.0  0.0  1.0

处理缺失数据

  • 滤除缺失数据,dropna()
  • 填充缺失数据,fillna()

使用DataFrame的列

人们经常想要将DataFrame的一个或多个列当做行索引来用,或者可能希望将行索引变成DataFrame的列。以下面这个DataFrame为例:

frame = 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]})
frame2 = frame.set_index(['c', 'd'])
print(frame2)

输出结果:

       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

reset_index的功能跟set_index刚好相反,层次化索引的级别会被转移到列里面:

print(frame2.reset_index())

输出结果:

     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部分的内容也结束了。

猜你喜欢

转载自blog.csdn.net/dylan_me/article/details/80931312