美文网首页
第5章 Pandas入门(1)

第5章 Pandas入门(1)

作者: whybask | 来源:发表于2019-10-27 11:15 被阅读0次

以下内容主要学习自《利用Python进行数据分析》

第5章 Pandas入门(1)

pandas所包含的数据结构和数据处理工具,使得在pandas中进行数据清洗和分析非常快捷。从2010年成为开源项目以来,pandas的开发者社区已经有超过800个代码贡献者,他们帮助pandas成熟、成为在数据领域被广泛应用的大型类库。

pandas支持大部分NumPy风格的数组计算,尤其是数据函数以及没有for循环的各种数据处理。但他们也有差异:

  • pandas是用来处理表格型或异质型数据的(想想关系型数据库中的表);
  • 而NumPy更适合处理同质型的、数值类的数组。

作为约定,我会使用下面的快捷方式导入pandas:

import pandas as pd

因此,只要你看到pd.,就表示对pandas的引用。

pandas-series-dataframe

数据结构

为了入门pandas,你需要熟悉两个常用的数据结构:Series和DataFrame。

为便于理解,可以把DataFrame看成是关系型数据库的一个表、把Serise看成其中的一个列。但需要警惕,它们并不是完全相同,如:尽管DataFrame通常情况下是二维的,但可以用分层索引在DataFrame中展现更高维度的数据。

Series

Series是一种一维的数组型对象,它包含了一个值序列,并包含了数据标签,称为索引(index)。

创建Series

从一个Python列表创建Series

In [1]: import pandas as pd

In [2]: obj = pd.Series([4, 7, -5, 3])

In [3]: obj
Out[3]:
0    4
1    7
2   -5
3    3
dtype: int64

如上所示,Series对象的索引在左侧、值在右侧,默认的索引是从0到n-1的数字索引(n是数据的总量)。如果不使用默认的数字索引,可以自定义标签索引,如下:

In [4]: obj = pd.Series([4,7, -5, 3], index=['d', 'b', 'a', 'c'])

In [5]: obj
Out[5]:
d    4
b    7
a   -5
c    3
dtype: int64

您还可以修改Series对象的标签索引,但必须保证索引个数与元素个数相等

# 为Series对象指定一个名字
In [6]: obj.name = 'my datas'

# 为Series对象指定新的标签索引
In [7]: obj.index = ['张三','李四','王五','杨六']

In [8]: obj
Out[8]:
张三    4
李四    7
王五   -5
杨六    3
Name: my datas, dtype: int64

如果已经有一个包含数据的Python字典,那么也可以生成一个Series,默认情况下字典的键将成为Series的标签索引,如下:

In [6]: dict_datas = {'Ohio':3500, 'Texas':7100, 'Oregon':1600}

In [7]: obj = pd.Series(dict_datas)

In [8]: obj
Out[8]:
Ohio      3500
Texas     7100
Oregon    1600
dtype: int64

把Python字典对象转换为Series对象时,仍然可以指定标签索引。Series将会自动匹配字典键,对于缺失的将赋值为NaN(not a number),如下:

In [1]: dic_data = {'2018':-0.715068, '2019':-0.068092,'2020':0.448901}

In [2]: years = ['2017','2018','2019','2020']

In [3]: obj = pd.Series(dic_data, index=years)

In [4]: obj  # 因为字典中没有2017,所以,被赋值为NaN
Out[4]:
2017         NaN
2018   -0.715068
2019   -0.068092
2020    0.448901
dtype: float64
访问Series

可以通过values属性index属性分别获得Series对象的值和索引:

In [1]: obj = pd.Series([4, 7, -5, 3], index=['d', 'b', 'a', 'c'])

In [2]: obj.values
Out[2]: array([ 4,  7, -5,  3], dtype=int64)

In [3]: obj.index
Out[3]: Index(['d', 'b', 'a', 'c'], dtype='object')

可以使用数字索引、也可以使用标签索引来获取Series中元素的值:

In [4]: obj[0]  # 使用数字索引,获取单个元素值
Out[4]: 4

In [5]: obj['d']  # 使用标签索引,获取单个元素值
Out[5]: 4

In [6]: obj[0:2]  # 使用数字索引,获取多个元素值
Out[6]:
d    4
b    7
dtype: int64

In [7]: obj['d':'b']  # 使用标签索引,获取多个元素值
Out[7]:
d    4
b    7
dtype: int64

某些情景中,您可以使用in关键字来判断Series的索引是否包含某个值:

In [8]: 'b' in obj
Out[8]: True

In [9]: 'e' in obj
Out[9]: False

还可以使用bool值作为滤条件,得到Series对象的子集:

In [10]: obj[obj > 0]  # 选择大于0的元素
Out[10]:
d    4
b    7
c    3
dtype: int64
修改Series的值

通过Series对象的索引(数字或标签均可),可以修改Series对象的值:

In [1]: obj = pd.Series([4, 7, -5, 3], index=['d', 'b', 'a', 'c'])

In [2]: obj['b'] = -1  # 通过标签索引修改值

In [3]: obj[2:4] = 0  # 通过数字索引段,修改值

In [4]: obj
Out[4]:
d    4
b   -1
a    0
c    0
dtype: int64
添加Series元素

如果要给Series对象添加单个元素,那么直接为新标签索引赋值即可:

In [5]: obj['e'] = 7

In [6]: obj
Out[6]:
d    4
b   -1
a    0
c    0
e    7
dtype: int64

使用Series对象的append方法可以把多个元素添加到现有的Series对象中:

In [7]: obj2 = pd.Serise([5, -2, 8])

In [8]: obj = obj.append(obj2)

In [9]: obj
Out[9]:
d    4
b   -1
a    0
c    0
e    7
0    5
1   -2
2    8
dtype: int64
删除Series元素

使用drop方法、并指定要删除的标签索引,将删除Series对象中的元素:

In [10]: obj.drop(labels=[0, 'c'])
Out[10]:
d    4
b   -1
a    0
e    7
1   -2
2    8
dtype: int64
运算

可以对Series对象进行四则运算,如下:

In [1]: obj = pd.Series([4, 7, -5, 3], index=['d', 'b', 'a', 'c'])

In [3]: obj * 2  # 与标量的四则运算,乘2。
Out[3]:
d     8
b    14
a   -10
c     6
dtype: int64

可以对Series对象应用数学函数,如下:

In [4]: import numpy as np

In [5]: np.exp(obj)  # 求自然指数
Out[5]:
d      54.598150
b    1096.633158
a       0.006738
c      20.085537
dtype: float64

两个Series对象可以用+操作符进行所谓的“数据对齐”操作,你可以认为这个特性与数据库的join操作相似。

In [1]: obj1 =  pd.Series([350, 160, 710, 500],
     ...:  index=['张三','李四', '王五', '杨六'])

In [2]: obj2 =  pd.Series([np.NaN, 350, 160],
     ...: index=['陈七', '张三', '李四'])

In [3]: obj = obj1 + obj2

In [4]: obj
Out[4]:
张三    700.0
李四    320.0
杨六      NaN
王五      NaN
陈七      NaN
dtype: float64

可以使用isnull方法notnull方法判断Series对象是否包含空值。这两个方法,即是Series的实例方法、也是pandas的顶层方法。

# 使用实例方法判断“空值”
In [5]: obj.isnull()
Out[5]:
张三    False
李四    False
杨六     True
王五     True
陈七     True
dtype: bool

# 使用pandas顶层方法判断“非空值”
In [6]: pd.notnull(obj)
Out[6]:
张三     True
李四     True
杨六    False
王五    False
陈七    False
dtype: bool

DataFrame

DataFrame表示的是矩阵的数据表,每一列可以是不同的数据类型。DataFrame既有行索引也有列索引。DataFrame的每一列都是一个Series对象。

尽管不完全正确,但可以用关系数据库表来类比:行索引相当于主键、列索引相当于列名。

尽管DataFrame是二维的,但你可以利用分层索引在DataFrame中展现更高维度的数据,将在后续章节进行讨论。

创建DataFrame

下面的例子演示了用ndarray创建一个最简单的DataFrame。

# 引用numpy、pandas
In [1]: import numpy as np
In [2]: import pandas as pd

# 构建了一个3X4的二维ndarray
In [3]: arr = np.arange(12).reshape(3,4)

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

# 用ndarray创建DataFrame
In [5]: df = pd.DataFrame(arr)

In [6]: df
Out[6]:
    0  1   2   3
0  0  1   2   3
1  4  5   6   7
2  8  9  10  11

注意上面的示例,DataFrame对象除了数据,还包含列索引和行索引,缺省情况下,它们都是从0开始编号的顺序值

如果我们不想使用默认的行/列索引,可以用index属性columns属性修改DataFrame对象的行/列索引。

In [7]: df.index = ['a','b','c']

In [8]: df.columns = ['cn1','cn2','cn3','cn4']

# 经过以上的修改,DataFrame有了新的行列索引
In [9]: df
Out[9]:
   cn1  cn2  cn3  cn4
a    0    1    2    3
b    4    5    6    7
c    8    9   10   11

# 可以给DataFrame的name属性赋值,类似于定义表名
In [10]: df.name = 'my datas'

当然,index、columns可以在构造时就指定,以下代码的效果与上面是一致的。

# 引用numpy、pandas
In [1]: import numpy as np
In [2]: import pandas as pd

# 构建了一个3X4的二维ndarray
In [3]: arr = np.arange(12).reshape(3,4)

# 用ndarray创建DataFrame
In [24]: df = pd.DataFrame(arr,
    ...: index = ['a','b','c'],
    ...: columns = ['cn1','cn2','cn3','cn4'])

In [25]: df
Out[25]:
   cn1  cn2  cn3  cn4
a    0    1    2    3
b    4    5    6    7
c    8    9   10   11

包含等长度列表或NumPy数组的字典,也可以用来构建DataFrame。字典的键将被pandas视为列名。请参考如下的代码:

In [1]: data = {'state': ['Ohio','Ohio','Ohio','Nevada','Nevada','Nevada'],
   ...: 'year': [2000,2001,2002,2001,2002,2003],
   ...: 'pop': [1.5,1.7,3.6,2.4,2.9,3.2]}

In [2]: df = pd.DataFrame(data)

In [3]: df
Out[3]:
   state  year  pop
0    Ohio  2000  1.5
1    Ohio  2001  1.7
2    Ohio  2002  3.6
3  Nevada  2001  2.4
4  Nevada  2002  2.9
5  Nevada  2003  3.2

用字典构建DataFrame时,如果传入columns参数,pandas会按照columns的顺序对列排序;如果字典的键不包含在colums中,pandas会填充缺失值,如下:

In [4]: df = pd.DataFrame(data, columns=['year','state','pop','debt'],
    ...: index=['one','tow','three','four','five','six'])

In [5]: df
Out[5]:
       year   state  pop debt
one    2000    Ohio  1.5  NaN
tow    2001    Ohio  1.7  NaN
three  2002    Ohio  3.6  NaN
four   2001  Nevada  2.4  NaN
five   2002  Nevada  2.9  NaN
six    2003  Nevada  3.2  NaN

在上面的示例中,我们还传入了index参数,用来取代pandas默认的、从0开始的数字行索引。

使用DataFrame构造函数创建DataFrame对象的有效输入小结如下:

类型 说明
2D ndarray 二维数组,行和列的标签是可选参数
数组、列表、元组构成的字典 每个序列成为DataFrame的一列,所有序列必须长度相同
NumPy结构化数组 与数组构成的字典一致
Series构成的字典 每个值成为一列,每个Series的索引联合起来形成行索引
字典构成的字典 每个内部字典成为一列,键联合起来形成行索引
字典或Series构成的列表 列表中的一个元素形成一行,字典键或Series索引联合起来形成列标签
列表或元组构成的列表 与2D ndarray一致
其它DataFrame 默认使用原DataFrame的行列索引
NumPy MaskedArray 与2D ndarry一致,但隐藏值会成为NaN缺失值
访问数据

在科学计算中,数据量往往非常大。如果仅要看看DataFrame的结构,可以调用head方法获取头部的5行数据

In [1]: data = {'state': ['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada', 'Nevada'], 'year': [2000, 2001, 2002, 2001, 2002, 2003], 'pop': [1.5, 1.7, 3.6, 2.4, 2.9, 3.2]}

In [2]: df = pd.DataFrame(data, columns=['year', 'state', 'pop'], index=['one', 'tow', 'three', 'four', 'five', 'six'])

In [3]: df
Out[3]:
       year   state  pop
one    2000    Ohio  1.5
tow    2001    Ohio  1.7
three  2002    Ohio  3.6
four   2001  Nevada  2.4
five   2002  Nevada  2.9
six    2003  Nevada  3.2

In [4]: df.head()
Out[4]:
       year   state  pop
one    2000    Ohio  1.5
tow    2001    Ohio  1.7
three  2002    Ohio  3.6
four   2001  Nevada  2.4
five   2002  Nevada  2.9

下面的示例演示了获取DataFrame的行数、列数、行索引、列索引等:

# 获取行索引
In [5]: df.index
Out[5]: Index(['ont', 'tow', 'three', 'four', 'five', 'six'], dtype='object')

# 得到行数
In [6]: len(df.index)
Out[6]: 6

# 获取列索引
In [7]: df.columns
Out[7]: Index(['year', 'state', 'pop'], dtype='object')

# 得到列数
In [8]: len(df.columns)
Out[8]: 3

获取行数据,采用与NumPy一致的索引方式,索引值可以是数字索引或标签索引,如下:

In [9]: df[1:3]  # 用数字索引获取第1、2行数据
Out[9]:
       year state  pop
two    2001  Ohio  1.7
three  2002  Ohio  3.6

In [10]: df['four':]  # 用标签索引获取行数据
Out[10]:
      year   state  pop
four  2001  Nevada  2.4
five  2002  Nevada  2.9
six   2003  Nevada  3.2

获取列数据,采用与字典键值索引一致的语法,按列索引得到的结果是Series对象:

In [11]: df['year']
Out[11]:
ont      2000
two      2001
three    2002
four     2001
five     2002
six      2003
Name: year, dtype: int64

获取单元数据,需要同时传入列索引和行索引,如下:

In [12]: df['year']['three']
Out[12]: 2002

获取切片数据,就是得到部分的行列数据子集,关键的语法是:起始行索引用冒号分割,列索引要明确指定并封装为一个列表。至于行索引在前还是列索引在前,都无所谓。如下:

In [13]: df['three':'five'][['state','pop']]
Out[13]:
        state  pop
three    Ohio  3.6
four   Nevada  2.4
five   Nevada  2.9

# 行列索引的先后次序没有关系,下面的语句与如上的等效
In [12]: df[['state','pop']]['three':'five']

调用DataFrame对象的values属性,可以得到ndarray类型的数据集(没有行列索引):

In [14]: df.values
Out[14]:
array([[2000, 'Ohio', 1.5],
       [2001, 'Ohio', 1.7],
       [2002, 'Ohio', 3.6],
       [2001, 'Nevada', 2.4],
       [2002, 'Nevada', 2.9],
       [2003, 'Nevada', 3.2]], dtype=object)
修改数据

初始化dataframe如下:

In [1]: data = {'state': ['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada', 'Nevada'], 'year': [2000, 2001, 2002, 2001, 2002, 2003], 'pop': [1.5, 1.7, 3.6, 2.4, 2.9, 3.2]}

In [2]: df = pd.DataFrame(data, columns=['year', 'state', 'pop'], index=['one', 'tow', 'three', 'four', 'five', 'six'])

In [3]: df
Out[3]:
       year   state  pop
one    2000    Ohio  1.5
tow    2001    Ohio  1.7
three  2002    Ohio  3.6
four   2001  Nevada  2.4
five   2002  Nevada  2.9
six    2003  Nevada  3.2

修改行数据,可以通过指定行索引为dataframe的行赋值,下面的例子把一个固定值赋值给行:

# 通过指定行索引为第一行数据赋值
In [4]: df[0:1] = 0

In [5]: df
Out[5]:
       year   state  pop
one       0       0  0.0
tow    2001    Ohio  1.7
three  2002    Ohio  3.6
four   2001  Nevada  2.4
five   2002  Nevada  2.9
six    2003  Nevada  3.2

您可以看出,上面的示例能够正确运行,按并不是一个好的示例。因为每列的数据类型并不相同,为某行的所有列赋值为同一个常量会导致有歧义。

更好些的方式,是为某行赋值一个列表,如下所示:

In [6]: df[0:1] = [2000,'Ohio',3.8]

In [7]: df
Out[7]:
       year   state  pop
one    2000    Ohio  3.8
tow    2001    Ohio  1.7
three  2002    Ohio  3.6
four   2001  Nevada  2.4
five   2002  Nevada  2.9
six    2003  Nevada  3.2

修改列数据
可以把常数赋值给某一列

In [8]: df['pop']=0

In [9]: df
Out[9]:
       year   state  pop
one    2000    Ohio    0
tow    2001    Ohio    0
three  2002    Ohio    0
four   2001  Nevada    0
five   2002  Nevada    0
six    2003  Nevada    0

也可以把一个列表赋值给某一列:

In [10]: df['pop']= [3.3, 1.2, 3.1, 1.9, 2.4, 2.7]

In [11]: df
Out[11]:
       year   state  pop
one    2000    Ohio  3.3
tow    2001    Ohio  1.2
three  2002    Ohio  3.1
four   2001  Nevada  1.9
five   2002  Nevada  2.4
six    2003  Nevada  2.7

还可以在列上直接使用标量运算:

In [12]: df['pop'] = df['pop'] * 3

In [13]: df
Out[13]:
       year   state  pop
one    2000    Ohio  9.9
tow    2001    Ohio  3.6
three  2002    Ohio  9.3
four   2001  Nevada  5.7
five   2002  Nevada  7.2
six    2003  Nevada  8.1

修改单元数据,需要同时指定行索引和列索引。
下面的示例演示了如何修改一个单元格数据

In [14]: df[0:1]['pop'] = 8.8
SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame.Try using .loc[row_indexer,col_indexer] = value instead

In [15]: df
Out[15]:
       year   state  pop
one    2000    Ohio  8.8
tow    2001    Ohio  3.6
three  2002    Ohio  9.3
four   2001  Nevada  5.7
five   2002  Nevada  7.2
six    2003  Nevada  8.1

请注意,虽然上面的语句可以完成单元格赋值,但引发了一个警告,该警告说明:比起使用上面的方法,使用.loc[row_indexer,col_indexer] = value更好,在下方的整数索引章节会有更详细的说明。

添加数据

初始化dataframe如下:

In [1]: data = {'state': ['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada', 'Nevada'], 'year': [2000, 2001, 2002, 2001, 2002, 2003], 'pop': [1.5, 1.7, 3.6, 2.4, 2.9, 3.2]}

In [2]: df = pd.DataFrame(data, columns=['year', 'state', 'pop'], index=['one', 'tow', 'three', 'four', 'five', 'six'])

In [3]: df
Out[3]:
       year   state  pop
one    2000    Ohio  1.5
tow    2001    Ohio  1.7
three  2002    Ohio  3.6
four   2001  Nevada  2.4
five   2002  Nevada  2.9
six    2003  Nevada  3.2

添加行数据
下面的示例使用字典对象为dataframe添加一行

In [4]: df.append({'year': 2001, 'state': 'Kilo', 'pop': 2.5}, ignore_index=True)
Out[4]:
   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
5  2003  Nevada  3.2
6  2001    Kilo  2.5

在上面的代码中,如果不使用ignore_index=True参数,会报告错误“TypeError: Can only append a Series if ignore_index=True or if the Series has a name”。

下面的代码演示如何用Series对象为dataframe添加行数据,其效果与上面是一样的。

In [5]: rd = pd.Series({'year': 2001, 'state': 'Kilo', 'pop': 2.5} , name='seven')

In [6]: df.append(rd)
Out[6]:
       year   state  pop
one    2000    Ohio  1.5
tow    2001    Ohio  1.7
three  2002    Ohio  3.6
four   2001  Nevada  2.4
five   2002  Nevada  2.9
six    2003  Nevada  3.2
seven  2001    Kilo  2.5

另外,如果两个dataframe的结构一致,也可以使用append方法追加行数据。

添加列数据
使用insert方法为dataframe对象添加一列。

# 把名为memo的数据添加到第二列
In [7]: df.insert(1, 'memo', ['m1','m2','m3','m4','m5','m6'])

In [8]: df
Out[8]:
       year memo   state  pop
one    2000   m1    Ohio  1.5
tow    2001   m2    Ohio  1.7
three  2002   m3    Ohio  3.6
four   2001   m4  Nevada  2.4
five   2002   m5  Nevada  2.9
six    2003   m6  Nevada  3.2
删除数据

使用drop方法删除行列,该方法中的参数axis为1表示删除列,0表示删除行。inplace为True表示直接对原表修改。

下面的示例演示了如何删除列

In [9]: df.drop('memo', axis=1, inplace=True)

In [10]: df
Out[10]:
       year   state  pop
one    2000    Ohio  1.5
tow    2001    Ohio  1.7
three  2002    Ohio  3.6
four   2001  Nevada  2.4
five   2002  Nevada  2.9
six    2003  Nevada  3.2

下面的示例演示了如何删除行

In [11]: df.drop(['two','three','five'], axis=0, inplace=True)

In [12]: df
Out[12]:
      year   state  pop
one   2000    Ohio  1.5
four  2001  Nevada  2.4
six   2003  Nevada  3.2
运算

使用dataframe对象的stack方法unstack方法可以行列转置,如下:

In [13]: df.stack()
Out[13]:
one   year       2000
      state      Ohio
      pop         1.5
four  year       2001
      state    Nevada
      pop         2.4
six   year       2003
      state    Nevada
      pop         3.2
dtype: object

In [14]: df.stack().unstack(0)
Out[14]:
        one    four     six
year   2000    2001    2003
state  Ohio  Nevada  Nevada
pop     1.5     2.4     3.2

index

索引对象

pandas中的索引对象是用于存储轴标签和其它元数据的。大多数时候,我们并不常用索引对象提供的功能,但是因为一些操作会产生包含索引化数据的结果,因此有必要理解索引是如何工作的。

在构造Series或DataFrame时,你所使用的任意数组或标签都可以在内部转换为索引对象

In [1]: import numpy as np

In [2]: import pandas as pd

In [3]: obj = pd.Series(range(3), index=['a', 'b', 'c'])

In [4]: index = obj.index

In [5]: index
Out[5]: Index(['a', 'b', 'c'], dtype='object')

In [6]: index[1:]
Out[6]: Index(['b', 'c'], dtype='object')

注意:索引对象是不可改变的,也就是说用户无法修改索引对象。不变性使得在多种数据结构中分享索引对象更为安全。另外,pandas索引对象可以包含重复的标签数据。

方法和属性

索引对象提供了一些方法和属性,如下表所示:

方法 描述
append 将额外的索引对象追加到原索引对象后,产生一个新的索引
difference 计算两个索引的差集
intersection 计算两个索引的交集
union 计算两个索引的并集
isin 计算表示每一个值是否在容器中
delete 将位置i的元素删除,并产生新的索引
drop 根据传参删除指定索引值,并产生新的索引
insert 在位置i处插入元素,并产生新的索引
is_monotonic 如果索引序列递增,返回True
is_unique 如果索引序列值唯一,返回True
unique 计算索引的唯一值序列

相关文章

网友评论

      本文标题:第5章 Pandas入门(1)

      本文链接:https://www.haomeiwen.com/subject/wsppvctx.html