Pandas是一个强大的分析结构化数据的工具集,它的使用基础是Numpy,用于数据挖掘和数据分析,同时也具有数据清洗功能。
Pandas也是Python数据分析和实战的必备工具包之一,它提供了快速灵活的数据结构,简单的直观的处理关系型数据。可以方便的处理像Excel或者数据库中这样的结构化的数据。Pandas是基于NumPy开发,并且是开源的分析工具。从0.25.x系列版本开始,Pandas仅支持Python 3.5.3及更高版本。未来的版本中将提高到3.6,在不管什么时候开始学习,可以选择使用最新版的Python和Pandas。
Panda的官网是https://pandas.pydata.org/ 同样也是纯英文的网站,有能力的读者在学习使用Pandas时,可以查阅官网的第一手资料。
开始正式介绍Pandas之前,先来举一个简单的例子。假设一个数学老师需要统计班级上期末考试的分数情况,一般的都是会看下班上最高分,最低分,平均分,中位分数等等各种数据指标,那么Pandas就可以一步到位处理。
#导入pandas库
import pandas as pd
#假设数据为数据成绩,以6个例子
data1 = [80, 88, 94, 85, 86, 90]
a = pd.Series(data1)
print(a.describe())
#代码运行结果:
count 6.000000
mean 87.166667
std 4.750439
min 80.000000
25% 85.250000
50% 87.000000
75% 89.500000
max 94.000000
dtype: float64
Pandas中describe()方法可以快速的描述一组数据的概况。基本内容是:count为元素个数,mean为平均数,std为标准差,min是最小值,max是最大值,25%、50%、75%表示的此分位数值。
当然在教学组长眼中,并不仅仅局限一个班的情况,可能是需要统计10个班甚至20个班的成绩情况,可以直接比较那个班级的分数情况高低。
#导入pandas库
import numpy as np
import pandas as pd
#每个数组代表一个班级成绩
data2 = [[80, 88, 94, 85, 86, 90],
[82, 81, 93, 92, 97, 78]]
#生成narray对象
a = np.array(data2)
#pandas加载对象
b = pd.DataFrame(a.T)
print(b.describe())
#代码运行结果:
0 1
count 6.000000 6.000000
mean 87.166667 87.166667
std 4.750439 7.782459
min 80.000000 78.000000
25% 85.250000 81.250000
50% 87.000000 87.000000
75% 89.500000 92.750000
max 94.000000 97.000000
当然Pandas的功能并不止于此,接下来将我们了解Pandas的基础知识点。
Series
在Pandas中,最常用的就是数据结构就是Series和DataFrame。Series是带标签的一维数组,可以储存的数字、字符串等常见对象。调用pd.Series()即可创建Series
#导入pandas库
import pandas as pd
data = [80, 88, 'a', 21.5]
a = pd.Series(data)
print(a)
print(type(a))
#代码输出结果:
0 80
1 88
2 a
3 21.5
dtype: object
<class 'pandas.core.series.Series'>
当打印Series对象a的时候左边会默认带着index,也就是索引,index也可以显式的指定,长度必须跟data长度一致,否则会报错:ValueError。当然即便是指定索引,也还是可以使用原来默认的索引查询数据。
import pandas as pd
data = [80, 88, 'a', 21.5]
a = pd.Series(data, index=['a', 'b', 'c', 'd'])
print(a)
print(a['b'])
print(a[0])
#代码输出结果:
a 80
b 88
c a
d 21.5
dtype: object
88
80
注意:在Pandas 中,索引值可以重复。
import pandas as pd
data = [80, 88, 'a', 21.5]
a = pd.Series(data, index=['a', 'b', 'b', 'd'])
print(a['b'])
#代码运行结果:
b 88
b a
dtype: object
另外还可以使用字典对象创建Series,key会成为index值,Value会成为data值
import pandas as pd
data = {'a': 90, 'b': 22.3, 'c': 'Python'}
a = pd.Series(data)
print(a)
print(a['b'])
print(a[0])
#代码运行结果:
a 90
b 22.3
c Python
dtype: object
22.3
90
如果字典对象中指定上index后,会根据指定的index值重排序。如果值缺少,Pandas会使用NaN(Not a Number)代替。
import pandas as pd
data = {'a': 90, 'b': 22.3, 'c': 'Python'}
a = pd.Series(data, index=['c', 'b', 'a', 'd'])
print(a)
#代码运行结果:
c Python
b 22.3
a 90
d NaN
dtype: object
Series 操作与 ndarray 类似,支持大多数 NumPy 中的函数,并且也支持索引切片。
import pandas as pd
data = {'a': 90, 'b': 22.3, 'c': 'Python'}
a = pd.Series(data, index=['c', 'b', 'a', 'd'])
print(a[0])
print(a[:2])#索引切片,取去a[0]-a[2]
#代码运行结果:
Python
c Python
b 22.3
dtype: object
另外在Series的初始化中还有一些其他属性:
__init__(self, data=None, index=None, dtype=None, name=None, copy=False,
fastpath=False)
除了data,index,上面见到过,dtype跟NumPy中的一样的,还有name属性,就是可以给当前的Series对象赋值一个名字。Copy是布尔值,如果为True,则拷贝输入数据。
import pandas as pd
data = [1, 2, 3]
a = pd.Series(data, name="num")
print("a对象的名称是:" + a.name)
#代码运行结果:
a对象的名称是:num
DataFrame
DataFrame是由多种类型的列构成的二维标签数据结构,可以理解做为Excel表格或者数据库中的表。DataFrame 是最常用的 Pandas 对象,与 Series 一样,DataFrame 支持多种类型的输入数据。
import pandas as pd
#使用Series 字典创建DataFrame
d = {'a': pd.Series([1, 2, 3]),
'b': pd.Series([4, 5, 6]),
'c': pd.Series([7, 8, 9])
}
df = pd.DataFrame(d)
print(df)
#代码运行结果:
a b c
0 1 4 7
1 2 5 8
2 3 6 9
DataFrame的结构可以比作excel表格的内容,当然也可以直接使用一个二维数组来生成DataFrame,比如:
import pandas as pd
d = {'a': pd.Series([1, 2, 3]),
'b': pd.Series([4, 5, 6]),
'c': pd.Series([7, 8, 9])
}
df = pd.DataFrame(d)
print(df)
#代码运行结果:
0 1 2
0 1 2 3
1 4 5 6
2 7 8 9
还可以是用列表字典来创建DataFrame
import pandas as pd
d = [{'a': 1, 'b': 2}, {'a': 4, 'b': 5, 'c': 6}, {'a': 7, 'c': 9}]
df = pd.DataFrame(d)
print(df)
#代码运行结果:
a b c
0 1 2.0 NaN
1 4 5.0 6.0
2 7 NaN 9.0
由此可以看出,DataFrame也是 Series 的容器,Series 则是标量的容器。DataFrame处理表格数据的时候,虽然也是二维数据,但是使用index(行)或 columns(列)比 axis 0(0轴) 和 axis 1 (1轴)更直观。用这种方式迭代 DataFrame 的列,可以使代码更整洁易读。
import pandas as pd
d = [{'a': 1, 'b': 2}, {'a': 4, 'b': 5, 'c': 6}, {'a': 7, 'c': 9}]
df = pd.DataFrame(d)
for col in df.columns:
print(type(df[col]))
print(df[col])
#代码运行结果:
<class 'pandas.core.series.Series'>
0 1
1 4
2 7
Name: a, dtype: int64
…
两者的数据结构差别如表所示
名称 | 维度 | 描述 |
---|---|---|
Series | 1 | 带标签的一维同构数组 |
DataFrame | 2 | 带标签的大小可变的二维异构表格 |
Pandas 所有数据结构的值都是可变的,数据结构的大小不都是可变的,Series 的长度不可改变,但是DataFrame里就可以插入新的列。
查看数据
在更多的时候,做数据分析,往往会从外部读取数据,常用的读取从excel表格数据,DataFrame可以便捷的去读excel数据。我们在5.1.4中已经抓取到了豆瓣TOP250的电影信息,并且将信息保存为movie.xlsx。以下示例中均是采用movie.xlsx文件的内容,读者可以先按照5.1.4小节的方法将数据抓取到本地。
import pandas as pd
df = pd.read_excel('movie.xlsx')
print(df)
代码运行结果:
import pandas as pd
df = pd.read_excel('movie.xlsx')
print(df.head())#查看 DataFrame 头部数据
print(df.tail(2)) #查看 DataFrame 尾部数据
代码运行结果:
使用head()和tail()方法可以查看头部和尾部的数,默认是5条,可以自定义设置条数。参数几为查看的条数。
import pandas as pd
df = pd.read_excel('movie.xlsx')
#DataFrame对象的基本信息
print(df.info())
#代码运行结果:
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 249 entries, 0 to 248
Data columns (total 8 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 电影名称 249 non-null object
1 上映年份 249 non-null int64
2 导演 249 non-null object
3 类型 249 non-null object
4 评价分数 249 non-null float64
5 评价人数 249 non-null int64
6 制片国家/地区 249 non-null object
7 语言 249 non-null object
dtypes: float64(1), int64(2), object(5)
memory usage: 15.7+ KB
None
在本小节开头的位置,提到了一个方法就是describe(),这是一个非常简洁的方法,可以快速的描述出数据的大体情况。因为dateframe默认会使用科学计数法,如果数据比较大,得出来数据不是很美观,所以可以设置pandas的参数,
import pandas as pd
df = pd.read_excel('movie.xlsx')
#为了格式化数据,不使用科学计数法,保留一位小数
pd.set_option('display.float_format', lambda x: '%.1f' % x)
print(df.describe())
#代码运行结果:
上映年份 评价分数 评价人数
count 249.0 249.0 249.0
mean 2000.5 8.9 586291.0
std 15.8 0.3 369609.5
min 1931.0 8.4 106212.0
25% 1995.0 8.7 341114.0
50% 2004.0 8.9 483692.0
75% 2011.0 9.1 702971.0
选择数据
选择单列,产生一个Series,使用[]切片选择行。
import pandas as pd
df = pd.read_excel('movie.xlsx')
#选择单列
print(df['上映年份'])
#选择前三行
print(df[0:3])
#代码运行结果:
0 2010
1 1988
2 2009
3 2002
4 1998
...
244 1988
245 2004
246 2001
247 2013
248 1997
Name: 上映年份, Length: 249, dtype: int64
电影名称 上映年份 导演 类型 评价分数 评价人数 制片国家/地区 语言
0 怦然心动 Flipped 2010 罗伯·莱纳 剧情 9.1 1457992 美国 英语
1 龙猫 となりのトトロ 1988 宫崎骏 动画 9.2 1031042 日本 日语
2 三傻大闹宝莱坞 3 Idiots 2009 拉吉库马尔·希拉尼 剧情 9.2 1538779 印度 印地语
除了使用[] 直接访问,还可以使用Pandas 优化过的数据访问方法,比如loc()和iloc()等,loc意思为location,是功能强大的选择方法。iloc中i的意思是指integer,所以它只接受整数作为参数。数值都是index的值,从0开始,即0表示第一行。
import pandas as pd
df = pd.read_excel('movie.xlsx')
# 选择第一行数据
print(df.loc[0])
print("--------------")
#选择第1-4行数据,包括第4(index=3)行数据的‘电影名称’
print(df.loc[0:3, '电影名称'])
print("--------------")
#选择第3-4行数据不包括第5(index=5)行,第1-3列数据不包括第4行(index=3)
print(df.iloc[2:4, 0:3])
#代码运行结果:
电影名称 怦然心动 Flipped
上映年份 2010
导演 罗伯·莱纳
类型 剧情
评价分数 9.1
评价人数 1457992
制片国家/地区 美国
语言 英语
Name: 0, dtype: object
--------------
0 怦然心动 Flipped
1 龙猫 となりのトトロ
2 三傻大闹宝莱坞 3 Idiots
3 无间道 無間道
Name: 电影名称, dtype: object
--------------
电影名称 上映年份 导演
2 三傻大闹宝莱坞 3 Idiots 2009 拉吉库马尔·希拉尼
3 无间道 無間道 2002 刘伟强
除了loc()和iloc(),还可以使用at()和iat(),作用是获取某个位置的值。
import pandas as pd
df = pd.read_excel('movie.xlsx')
print(df)
# 获取index是3 column是'电影名称'的值
print(df.at[3, '电影名称'])
# 获取index是2 ,第2列的内容
print(df.iat[2, 1])
#代码运行结果:
无间道 無間道
2009
在选择或者查询数据的时候,肯定会带又一些条件,这时候我们可以直接选择某一个列,进行条件筛选,得到想要的数据,比如说,我们想查一下最近10年上映的评分高于9分的电影。
import pandas as pd
df = pd.read_excel('movie.xlsx')
print(df[df.上映年份 > 2016][df.评价分数 > 9.0])
print(len(df[df.评价分数 > 9.0]))
#代码运行结果:
电影名称 上映年份 导演 类型 评价分数 评价人数 制片国家/地区 语言
36 何以为家 كفرناحوم 2018 娜丁·拉巴基 剧情 9.1 807449 黎巴嫩 阿拉伯语
45 寻梦环游记 Coco 2017 李·昂克里奇 喜剧 9.1 1276741 美国 英语
103 人生果实 人生フルーツ 2017 伏原健之 纪录片 9.5 131655 日本 日语
这样可以通过设置条件选择出来我们想要的数据,或者统计出来简单的数据结果,比如说分别统计一下从20世纪30年代到21世纪20年代,这100年中的高质量影片数量分布情况,看下哪个10年的电影文化产业发展的更好,还可以结合当时的历史背景等等得出一些结论。这就是做了一次简单的数据分析,感兴趣的读者可以尝试一下。
转置数据
import pandas as pd
df = pd.read_excel('movie.xlsx')
#直接使用.T获得转置数据
print(df.T)
#代码运行结果:
0 1 ... 247 248
电影名称 怦然心动 Flipped 龙猫 となりのトトロ ... 再次出发之纽约遇见你 Begin Again 千钧一发 Gattaca
上映年份 2010 1988 ... 2013 1997
导演 罗伯·莱纳 宫崎骏 ... 约翰·卡尼 安德鲁·尼科尔
类型 剧情 动画 ... 喜剧 剧情
评价分数 9.1 9.2 ... 8.6 8.8
评价人数 1457992 1031042 ... 343860 209917
制片国家/地区 美国 日本 ... 美国 美国
语言 英语 日语 ... 英语 英语
[8 rows x 249 columns]
排序数据
Pandas中排序使用的是sort_values(),详细方法和主要使用的参数如下:
sort_values(by,axis=0,ascending=True,inplace=False,kind="quicksort",na_position="last",ignore_index=False)
by:依照排序的列
ascending:bool型,True为升序,False为倒叙。默认为升序
kind:排序方法,{‘quicksort’, ‘mergesort’, ‘heapsort’}, 默认是使用‘quicksort’。
import pandas as pd
#为了方便查看,选择Excel表格中的指定列
df = pd.read_excel('movie.xlsx', usecols=['电影名称', '上映年份', '制片国家/地区'])
print(df.sort_values(by="上映年份"))
#代码运行结果:
电影名称 上映年份 制片国家/地区
207 城市之光 City Lights 1931 美国
82 摩登时代 Modern Times 1936 美国
23 乱世佳人 Gone with the Wind 1939 美国
190 魂断蓝桥 Waterloo Bridge 1940 美国
194 罗生门 羅生門 1950 日本
.. ... ... ...
185 小偷家族 万引き家族 2018 日本
191 波西米亚狂想曲 Bohemian Rhapsody 2018 英国
145 头号玩家 Ready Player One 2018 美国
55 绿皮书 Green Book 2018 美国
183 寄生虫 기생충 2019 韩国
[249 rows x 3 columns]
通过对TOP250高分电影的上映年代排序,可见排名前三的都是美国出品的,而且时间都是在19世纪40年代,可见美国的电影事业起步还是比较早,当时中国还是处于动乱时代,对于电影这种文化产业的发展根本没有基础。
如果设置ascending为False,则是倒叙排列,如果将by设置为“评价分数”,则是以分数排序,同样可以设置两个排序维度。下面演示一下,根据上映年份和评价分数两个维度来进行排序。
import pandas as pd
#注意这里需要添加‘评价分数’,不然会报keyError错误
df = pd.read_excel('movie.xlsx', usecols=['电影名称', '上映年份', '评价分数', '制片国家/地区'])
print(df.sort_values(by=['上映年份', '评价分数']))
#代码运行结果:
电影名称 上映年份 评价分数 制片国家/地区
207 城市之光 City Lights 1931 9.3 美国
82 摩登时代 Modern Times 1936 9.3 美国
23 乱世佳人 Gone with the Wind 1939 9.3 美国
190 魂断蓝桥 Waterloo Bridge 1940 8.8 美国
194 罗生门 羅生門 1950 8.8 日本
.. ... ... ... ...
191 波西米亚狂想曲 Bohemian Rhapsody 2018 8.7 英国
55 绿皮书 Green Book 2018 8.9 美国
24 我不是药神 2018 9.0 中国大陆
36 何以为家 كفرناحوم 2018 9.1 黎巴嫩
183 寄生虫 기생충 2019 8.7 韩国
[249 rows x 4 columns]
得到的结果是先按照上映年份升序排序,在按照评价分数升序排序。当然可以反过来,只不过需要在by参数列表中,更换下排序列的顺序。
有时候我们拿到的原始数据的质量并不好,有很多缺失值,这是很正常的情况。通常,有两个处理方法,第一个是去掉缺失值,如果某一条数据中是NaN,那么就去掉这一条,使用dropna()方法。另外一个就是将缺失值按照默认值填充,使用filln()方法。这两种方法都可以按照具体的情况来处理。
import pandas as pd
# 构建原始数据
data = {'a': [1, 2, 3],
'b': [4, None, 7],
'c': [7, 8, None]
}
df = pd.DataFrame(data)
print("原始数据:")
print(df)
print("删除缺失值:")
print(df.dropna())
print("填充缺失值:值为1")
print(df.fillna(value=1))
#代码运行结果:
原始数据:
a b c
0 1 4.0 7.0
1 2 NaN 8.0
2 3 7.0 NaN
删除缺失值:
a b c
0 1 4.0 7.0
填充缺失值:值为1
a b c
0 1 4.0 7.0
1 2 1.0 8.0
2 3 7.0 1.0
数据统计
在Series中有一个很常用的方法就是value_counts(),它的作用就是统计Series中每个元素出现的次数。比如,在movie.xlsx中已经是250 部高分电影的数据,我们想知道这些电影都是哪些国家制作的,哪些年份上映的,我们就可以通过value_counts()方法来统计。
import pandas as pd
df = pd.read_excel('movie.xlsx')
#统计每年上映的电影数的前十年份
print(df['上映年份'].value_counts()[:10])
#统计制作国家的电影数的前十国家/地区
print(df['制片国家/地区'].value_counts()[:10])
#代码运行结果:
2010 13
2004 13
1994 12
2013 11
2003 10
2008 10
2001 9
2006 9
2009 9
2014 9
Name: 上映年份, dtype: int64
美国 111
日本 34
中国香港 20
英国 17
中国大陆 15
韩国 11
法国 7
意大利 6
中国台湾 6
德国 5
Name: 制片国家/地区, dtype: int64
通过对年份统计结果,可以粗略的看出来2010年、2004年和1994年上映电影的质量都很高。而通过对制作国家的统计,看出来TOP250部高分电影中,有111部是美国制作。数量远高于第二名日本的34部。可见在电影文化产业,美国发展起步早,制作水平是世界领先水平。
这是通过value_counts()方法可以统计出来的原始数据,如果作为数据分析的结果呈现出来,稍微有点生涩,后面我们可以使用matlab来绘制更直观的图表。
数据整合
前面说过可以把dateframe看出是SQL表数据,那么在SQL中常用的连接、聚合等操作在Pandas中也是可以实现的。
import pandas as pd
data1 = {'a': [1, 2], 'b': [3, 4]}
data2 = {'a': [1, 3], 'b': [7, 8]}
#dateframe对象A比作表A
A = pd.DataFrame(data1)
#dateframe对象B比作表B
B = pd.DataFrame(data2)
print(A)
print(B)
#代码运行结果:
a b
0 1 3
1 2 4
a b
0 1 7
1 3 8
可以简单的看为,A表中字段名为a和b,有两行数据。B表中同样字段名为a和b也有两行数据,接下来我们做一个连接操作,就是SQL语句中的join操作。
import pandas as pd
data1 = {'a': [1, 2], 'b': [3, 4]}
data2 = {'a': [1, 3], 'b': [7, 8]}
a = pd.DataFrame(data1)
b = pd.DataFrame(data2)
# 以a列为准,默认是内连接 (inner join)
print("内连接")
print(pd.merge(a, b, on="a"))
# 以a列为准,连接方式为左连接(left join)
print("左连接")
print(pd.merge(a, b, on="a", how="left"))
# 以a列为准,连接方式为右连接(right join)
print("右连接")
print(pd.merge(a, b, on="a", how="right"))
# 以a列为准,连接方式为外连接(outer join)
print("外连接")
print(pd.merge(a, b, on="a", how="outer"))
#代码运行结果:
内连接
a b_x b_y
0 1 3 7
左连接
a b_x b_y
0 1 3 7.0
1 2 4 NaN
右连接
a b_x b_y
0 1 3.0 7
1 3 NaN 8
外连接
a b_x b_y
0 1 3.0 7.0
1 2 4.0 NaN
2 3 NaN 8.0
结果跟SQL中的连接操作是一致的。内连接得到两个对象中都有的数据,对象A中a列和对象B中的a列都有1。左连接以对象A的a列为准,对象B中a列中没有的值,则取空。右连接则以对象B的a列为准。外连接则查询出全部的数据。
同样除了连接操作还有聚合操作,与SQL中的使用groupby对列进行聚合操作一样。
import pandas as pd
data1 = {'a': [1, 2], 'b': [3, 4]}
data2 = {'a': [1, 3], 'b': [7, 8]}
a = pd.DataFrame(data1)
b = pd.DataFrame(data2)
# 连接两个对象做为一个对象
print(pd.concat([a, b]))
# 按照a列统计计数
print(pd.concat([a, b]).groupby('a').count())
# 按照a列求和
print(pd.concat([a, b]).groupby('a').sum())
#代码运行结果:
a+b
a b
0 1 3
1 2 4
0 1 7
1 3 8
计数
b
a
1 2
2 1
3 1
求和
b
a
1 10
2 4
3 8
熟悉SQL查询的读者对这个操作并不会很陌生,操作等价于下面使用SQL语句查询的结果。可以直观的看出,count()按照a列的值计数,值为1的有2个,值为2,3的有1个。Sum()操作在实际应用场景中通过会用于按照月份或者年度统计销售额等等。
--按照a列统计计数
Select a, count(a) from A group by a ;
--按照a列统计计数
Select a, sum(b) from A group by a ;