用于数据探索和分析,基于numpy
模块
安装
pip install pandas
常用操作
Series
序列(可以理解为一列数据),举例:
>>> a = pandas.Series([2,1,6,3])
>>> pandas.Series([2,1,6,3])
0 2
1 1
2 6
3 3
dtype: int64
注:
上面没有指定索引,所以默认索引就为从0开始的数字,也可以自己指定索引,举例:
>>> pandas.Series([2,1,6,3], index=['a','b','c','d'])
a 2
b 1
c 6
d 3
dtype: int64
DataFrame
数据框(行列都有的数据),举例:
>>> a = pandas.DataFrame([[1,2,5,3],[3,6],[3,5,1,2,6]]) #里面不一定得数字
>>> a
0 1 2 3 4
0 1 2 5.0 3.0 NaN
1 3 6 NaN NaN NaN
2 3 5 1.0 2.0 6.0
#最上面一行代表每一列序号,最左边一列代表每一行序号
注:
最后面有介绍DataFrame和Series的关系以及一些踩坑注意点
取值
按先列后行顺序取值,而且空的地方虽然为NaN,但是NaN之间是不等的,对上面的a举例:
>>> a[2][1] #这里不是[1][2],是先列后行
nan
>>> a[3][1]
nan
>>> a[3][1] == a[3][1]
False #可以看出两个NaN不等
注:
在pandas里异常的数据(如空)用NaN
表示,以及无穷数用inf
进行表示,此时对于NaN
可以通过pandas下的isnull
方法或者numpy下的isnan
方法判断,而inf
则通过numpy下的isinf
判断,举例:
>>> a = pandas.DataFrame([[1,2,5,3],[3,6, float("inf")],[3,5,1,2,6]])
>>> a
0 1 2 3 4
0 1 2 5.000000 3.0 NaN
1 3 6 inf NaN NaN
2 3 5 1.000000 2.0 6.0
>>> a.isnull()
0 1 2 3 4
0 False False False False True
1 False False False True True
2 False False False False False
>>> np.isnan(a)
0 1 2 3 4
0 False False False False True
1 False False False True True
2 False False False False False
>>> np.isinf(a)
0 1 2 3 4
0 False False False False False
1 False False True False False
2 False False False False False
切片操作
使用iloc
方法可以对DataFrame数据进行切片操作,使用切片操作时的顺序是先行后列,举例:
>>> a
0 1 2 3 4
0 1 2 5.0 3.0 NaN
1 3 6 NaN NaN NaN
2 3 5 1.0 2.0 6.0
>>> a.iloc[2, 3:]
# 去第三行第四列以后的数据
3 2.0
4 6.0
Name: 2, dtype: float64
使用ix
方法也可以进行切片操作,而且支持更加强大的筛选数据方式,如按列名筛选,举例:
>>> a = pandas.DataFrame([[1,2,5,3],[3,6],[3,5,1,2,6]])
>>> a.columns = ['a', 'b', 'c', 'd', 'e']
# 设置列名
>>> a
a b c d e
0 1 2 5.0 3.0 NaN
1 3 6 NaN NaN NaN
2 3 5 1.0 2.0 6.0
>>> a.ix[:, a.columns == 'c']
# 索引所有行,且列名为c的那一列
c
0 5.0
1 NaN
2 1.0
按条件筛选
举例:
>>> a = pandas.DataFrame([[1,2,5,3],[3,6],[3,5,1,2,6]])
>>> a
0 1 2 3 4
0 1 2 5.0 3.0 NaN
1 3 6 NaN NaN NaN
2 3 5 1.0 2.0 6.0
>>> a[a[4].notnull()]
# 筛选第四列不为NaN的数据
0 1 2 3 4
2 3 5 1.0 2.0 6.0
>>> a[a[1] > 2]
# 筛选第一列大于2的数据
0 1 2 3 4
1 3 6 NaN NaN NaN
2 3 5 1.0 2.0 6.0
>>> a.T[a.T[0].notnull()].T
# 转置后筛选第0列不为NaN的数据,再转回来,即筛选第0行不为NaN的列
0 1 2 3
0 1.0 2.0 5.0 3.0
1 3.0 6.0 NaN NaN
2 3.0 5.0 1.0 2.0
注:
筛选后的数据不能够直接通过索引或者切片来进行某列的数据修改,因此需要使用到loc
方法,要注意的是如果直接对索引后的数据进行赋值将会对整行的所有列都进行修改,举例:
>>> a = pandas.DataFrame([[1,2,5,3],[3,6],[3,5,1,2,6]])
>>> a
0 1 2 3 4
0 1 2 5.0 3.0 NaN
1 3 6 NaN NaN NaN
2 3 5 1.0 2.0 6.0
>>> a[3] != 3.
# 筛选第三列不为3的数据
0 False
1 True
2 True
Name: 3, dtype: bool
>>> a[a[3] != 3.]
# 索引筛选后的数据
0 1 2 3 4
1 3 6 NaN NaN NaN
2 3 5 1.0 2.0 6.0
>>> a.loc[a[3] != 3., 2] = 100.
# 修改筛选后数据的第三列为100.
>>> a
0 1 2 3 4
0 1 2 5.0 3.0 NaN
1 3 6 100.0 NaN NaN
2 3 5 100.0 2.0 6.0
>>> a[a[3] != 3.] = 200.
# 直接对筛选的数据赋值将会赋值到整行的所有数据
>>> a
# 可以看到2、3行全变成了200.
0 1 2 3 4
0 1.0 2.0 5.0 3.0 NaN
1 200.0 200.0 200.0 200.0 200.0
2 200.0 200.0 200.0 200.0 200.0
列名定义
普通定义举例:
>>> b = pandas.DataFrame([[1,2,5,3],[3,6,'s'],[3,5,1,2,6]], columns=['one','two','three','four','five'])
>>> b
one two three four five
0 1 2 5 3.0 NaN
1 3 6 s NaN NaN
2 3 5 1 2.0 6.0
>>> b['one'][2]
3
字典定义举例,举例:
>>> pandas.DataFrame({"a":1,"b":[2,3],"c":'321'})
a b c
0 1 2 321
1 1 3 321
可以看出a、c列里面因为只有一个元素,所以他们会自动重复复制补全。如果想要把某一列转成列表形式,可以用tolist()
,举例:
>>> c = pandas.DataFrame([[1,2,5,3],[3,6],[3,5,1,2,6]]) #里面不一定得数字
>>> c[0].tolist()
[1, 3, 3]
>>> type(c[0].tolist())
<class 'list'>
转出格式
DataFrame数据可以直接输出成字典、CSV、excel等格式,举例:
c.to_list() #转列表
c.to_dict() #转成字典
c.to_csv("E:/a.csv") #输出csv文件
c.to_excel("E:/abc.xlsx") #输出excel文件
c.to_html("E:/abc.html") #输出html文件
c.to_json("E:/abc.json") #输出json
…
替换数据
使用replace
方法可以将指定的一堆数据替换成其他数据,举例:
>>> a = pandas.DataFrame([[1,2,5,3],[3,6],[3,5,1,2,6]])
>>> a
0 1 2 3 4
0 1 2 5.0 3.0 NaN
1 3 6 NaN NaN NaN
2 3 5 1.0 2.0 6.0
>>> a.replace([1., 3.], 100.)
# 将1和3都替换成100
0 1 2 3 4
0 100 2 5.0 100.0 NaN
1 100 6 NaN NaN NaN
2 100 5 100.0 2.0 6.0
逐列操作
通过apply
方法可以对数据进行逐列操作,举例:
>>> a = pandas.DataFrame([[1,2,5,3],[3,6],[3,5,1,2,6]])
>>> a
0 1 2 3 4
0 1 2 5.0 3.0 NaN
1 3 6 NaN NaN NaN
2 3 5 1.0 2.0 6.0
>>> a.apply(lambda x: x+1)
# 对每列的数据都加1
0 1 2 3 4
0 2 3 6.0 4.0 NaN
1 4 7 NaN NaN NaN
2 4 6 2.0 3.0 7.0
该方法默认是在维度为0的方向进行操作,即基于列操作,如果希望基于行操作,则可以设置属性axis=1
head()
头部数据,也就是前几行的,括号里写行数,不写默认显示前5行,如果要取后几行就用tail()
describe()
按列统计情况,举例:
>>> c = pandas.DataFrame([[1,2,5,3],[3,6,'s'],[3,5,1,2,6]], columns=['one','two','three','four','five'])
>>> c.describe()
one two four five
count 3.000000 3.000000 2.000000 1.0 #count说明这一列有几个元素
mean 2.333333 4.333333 2.500000 6.0 #这一列的平均数
std 1.154701 2.081666 0.707107 NaN #标准差
min 1.000000 2.000000 2.000000 6.0 #最小值
25% 2.000000 3.500000 2.250000 6.0 #前%25分位数,和下面两行都是分位数
50% 3.000000 5.000000 2.500000 6.0
75% 3.000000 5.500000 2.750000 6.0
max 3.000000 6.000000 3.000000 6.0 #最大值
可以看出因为three
那列有非数字内容存在,所以不会被统计
矩阵转置T
转置,就是行和列对调,举例:
>>> c = pandas.DataFrame({"a":1,"b":[2,3],"c":'321'})
>>> c
a b c
0 1 2 321
1 1 3 321
>>> c.T
0 1
a 1 1
b 2 3
c 321 321
导入文件
csv read_csv()
excel read_excel()
html read_html()
txt read_table()
例如:data = pandas.read_csv("a.csv")
,像excel
文件如果不想要表头(第一行)可以设置属性header=None
,读取指定sheet可以设置sheetname
属性,举例:
data = pandas.read_excel("a.xls", header=None, sheetname='test')
其中,html的特别厉害,他可以自动帮你获取一个网页里table
标签的所有信息然后格式化打印出来,举例:
res = requests.get('https://wenku.baidu.com/list/202') #这网页有嵌在表格里的内容
content = res.content.decode('gb2312') #因为直接输出乱码,先转字节流再解码
c = pandas.read_html(content)
print(c) #你会惊叹,虽然本质其实就是自动定位table标签的内容
注:
要使用read_excel()
方法还需要:pip install xlrd
导入mysql
数据
这块特殊讲,使用的是pymysql
模块,可以参照前面,使用举例:
conn = pymysql.connect(host="127.0.0.1", port=3306, user="root", passwd="123456", db="test")
cursor = conn.cursor()
sql = "select * from user"
k = pandas.read_sql(sql, conn) #第一个参数查询语句、第二个是游标
print(k)
结果为:
name password nickname
0 a v c
1 aaa 111 dawson
2 abc aaa abc
3 asd sfa fas
4 ch ch ch
注:
对于文件和mysql导入的数据,返回的类型是DataFrame
,所以我们可以通过.shape
来看有几行几列,通过.values
来看其第几行第几列的对应信息,比如看第一行的就.value[0]
,第二行第一列就.value[1][0]
,还可以通过columns
看有那些列,举例:
>>> c = pandas.DataFrame([[1,2,5,3],[3,6,'s'],[3,5,1,2,6]], columns=['one','two','three','four','five'])
>>> c
one two three four five
0 1 2 5 3.0 NaN
1 3 6 s NaN NaN
2 3 5 1 2.0 6.0
>>> c['one']
0 1
1 3
2 3
>>> c['one'][1] #因为数据框必须先选列然后选行,所以不能只取某一行的数据
3
>>> c.values
array([[1, 2, 5, 3.0, nan],
[3, 6, 's', nan, nan],
[3, 5, 1, 2.0, 6.0]], dtype=object)
>>> c.values[0] #values是先选行在选列,所以可以只取一行
array([1, 2, 5, 3.0, nan], dtype=object)
>>> c.values[0][3] #values选列时是按0,1,2那样定位,不是按列名定位
3.0
>>> c.columns
Index(['one', 'two', 'three', 'four', 'five'], dtype='object')
甚至可以看、改变哪一列的哪个值的信息,比如对于上面数据库导入的信息:
k['name']
获得的就是name这一列的信息:
0 a
1 aaa
2 abc
3 asd
4 ch
Name: name, dtype: object
然后输入:
k['name'][k['name']=='asd']='ccc'
#将name这一列中值为asd的改为ccc,这里用k['name'][3]效果也一样
结果为:
0 a
1 aaa
2 abc
3 ccc
4 ch
Name: name, dtype: object
sort_values(by=哪一列)
根据某一列的值来排序,举例:
>>> c = pandas.DataFrame([[2,1,4,3],[1,4,6,2]])
>>> c
0 1 2 3
0 2 1 4 3
1 1 4 6 2
>>> c.sort_values(by=0)
0 1 2 3
1 1 4 6 2
0 2 1 4 3
data['列名'].unique()
计算某一列有多少种不同的元素,对上面的数据库数据举例:
>>> k['name'].unique()
array(['a', 'aaa', 'abc', 'ccc', 'ch'], dtype=object)
cut(数据, 份数[, labels=[每列名称]])
可以将数据分成好几份,会自动计算判断这块数据该处于哪份当中,举例:
>>> a = [0,2,3,93,4,5,70,16,48,76,25,30,31,50,51,100]
>>> c = pandas.cut(a, 4, labels=['低', '中', '高', '特高']) #分成4份
>>> c
[低, 低, 低, 特高, 低, ..., 中, 中, 中, 高, 特高]
Length: 16
Categories (4, object): [低 < 中 < 高 < 特高]
>>> for each in c:
print(each,end='\t')
低 低 低 特高 低 低 高 低 中 特高 低 中 中 中 高 特高
上面的如果没有第三个参数,其就会按数值区间来区分,可以发现其是根据最大值和最小值来进行等宽处理,从而分块:
>>> pandas.cut(a, 4)
[(-0.1, 25.0], (-0.1, 25.0], (-0.1, 25.0], (75.0, 100.0], (-0.1, 25.0], ..., (25.0, 50.0], (25.0, 50.0], (25.0, 50.0], (50.0, 75.0], (75.0, 100.0]]
Length: 16
Categories (4, interval[float64]): [(-0.1, 25.0] < (25.0, 50.0] < (50.0, 75.0] < (75.0, 100.0]]
#能看出极差是100,所以每25分一块
如果不想按均分的范围来分块,我们可以自定义,此时只要第二个参数改成列表形式就行了,举例:
>>> pandas.cut(a, [0, 10, 50, 100]) #自定义分了3个区间
[NaN, (0, 10], (0, 10], (50, 100], (0, 10], ..., (10, 50], (10, 50], (10, 50], (50, 100], (50, 100]]
Length: 16
Categories (3, interval[int64]): [(0, 10] < (10, 50] < (50, 100]]
#因为0不在范围内,所以是NaN
此时如果想看分块情况可以用describe()
,举例:
>>> b = pandas.cut(a, [0, 10, 50, 100]) #自定义分了3个区间
>>> b.describe()
counts freqs
categories
(0, 10] 4 0.2500
(10, 50] 6 0.3750
(50, 100] 5 0.3125
NaN 1 0.0625
打印行数省略问题
pandas和numpy一样,当数据过多时,打印就会以省略号形式展示,此时可以通过set_option
方法设置多少行以内的数据不以省略号进行展示,举例:
>>> pd.set_option('display.max_rows', 10)
# 超过10行就省略形式打印
>>> pd.DataFrame([[i] for i in range(100)])
0
0 0
1 1
2 2
3 3
4 4
.. ..
95 95
96 96
97 97
98 98
99 99
[100 rows x 1 columns]
>>> pd.set_option('display.max_columns', 10)
# 超过10列就省略形式打印
>>> pd.DataFrame([[j for j in range(20)] for i in range(100)])
0 1 2 3 4 ... 15 16 17 18 19
0 0 1 2 3 4 ... 15 16 17 18 19
1 0 1 2 3 4 ... 15 16 17 18 19
2 0 1 2 3 4 ... 15 16 17 18 19
3 0 1 2 3 4 ... 15 16 17 18 19
4 0 1 2 3 4 ... 15 16 17 18 19
.. .. .. .. .. .. ... .. .. .. .. ..
95 0 1 2 3 4 ... 15 16 17 18 19
96 0 1 2 3 4 ... 15 16 17 18 19
97 0 1 2 3 4 ... 15 16 17 18 19
98 0 1 2 3 4 ... 15 16 17 18 19
99 0 1 2 3 4 ... 15 16 17 18 19
[100 rows x 20 columns]
>>> pd.set_option('display.max_rows', None)
# 不管多少行都不省略打印
DataFrame和Series关系以及使用注意(小心踩坑)
关系
在pandas中,这两者的关系可以理解成:DataFrame是Series的集合,DataFrame中的每一行或者每一列都可以看做是一个Series。如何区分DataFrame和Series呢,在打印的时候就可以看出来,例如下面:
>>> a = pd.DataFrame([[j for j in range(10)] for i in range(100)])
>>> a
0 1 2 3 4 5 6 7 8 9
0 0 1 2 3 4 5 6 7 8 9
1 0 1 2 3 4 5 6 7 8 9
2 0 1 2 3 4 5 6 7 8 9
3 0 1 2 3 4 5 6 7 8 9
4 0 1 2 3 4 5 6 7 8 9
.. .. .. .. .. .. .. .. .. .. ..
95 0 1 2 3 4 5 6 7 8 9
96 0 1 2 3 4 5 6 7 8 9
97 0 1 2 3 4 5 6 7 8 9
98 0 1 2 3 4 5 6 7 8 9
99 0 1 2 3 4 5 6 7 8 9
[100 rows x 10 columns]
>>> type(a)
# a是个DataFrame
<class 'pandas.core.frame.DataFrame'>
>>> a[0]
# 取a的第一列
0 0
1 0
2 0
3 0
4 0
..
95 0
96 0
97 0
98 0
99 0
Name: 0, Length: 100, dtype: int64
>>> type(a[0])
# 可以看到第一列是个Series
<class 'pandas.core.series.Series'>
>>> a.iloc[0, :]
# 取a的第一行
0 0
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
Name: 0, dtype: int64
>>> type(a.iloc[0, :])
# 可以看到第一行也是个Series
<class 'pandas.core.series.Series'>
上面代码证明了每一行每一列都是Series,然后多个Series组成了DataFrame,而且打印的时候也可以看出:DataFrame的打印结果里面有多行多列,而且一般会有像[100 rows x 10 columns]
这样的行列数提示;而Series的打印中一般会显示像Name: 0, Length: 100, dtype: int64
这样的Name以及数据类型的提示等
注意
为什么这里要介绍两者的关系呢?因为这里面可能有个坑需要注意,假如我们想把两个DataFrame的某些列合并起来,将可能遇到一些坑,这里介绍两种方式实现,以及对应可能有的坑:
1. 通过zip
映射合并
(一般推荐使用第二种方式合并,只是使用zip
的方式有个比较明显的坑,所以先举例),举例:
>>> a = pd.DataFrame([[j for j in range(10)] for i in range(100)])
# 定义a和b两个DataFrame
>>> b = pd.DataFrame([[j*10 for j in range(10)] for i in range(100)])
>>> pd.DataFrame(list(zip(a.iloc[:, [1, 2]], b[0])))
# 将a的2/3列与b的第一列合并,发现结果竟然是这个?
0 1
0 1 0
1 2 0
>>> pd.DataFrame(list(zip(a.iloc[:, 1], b[0])))
# 再看合并a的第二列和b的第一列是没问题的
0 1
0 1 0
1 1 0
2 1 0
3 1 0
4 1 0
.. .. ..
95 1 0
96 1 0
97 1 0
98 1 0
99 1 0
[100 rows x 2 columns]
可以看出上面将多列和单列合并失败,竟然只有几行?究其原因,最后发现是DataFrame和Series是不同类型的,因此通过zip不能直接合并(后来尝试了以后证明:DataFrame之间也不能这样直接合并),所以要合并的时候需要通过Series来合并,例如上面的代码就要修改成:
>>> pd.DataFrame(list(zip(a[1], a[2], b[0])))
# 分成3个Series来合并
0 1 2
0 1 2 0
1 1 2 0
2 1 2 0
3 1 2 0
4 1 2 0
.. .. .. ..
95 1 2 0
96 1 2 0
97 1 2 0
98 1 2 0
99 1 2 0
[100 rows x 3 columns]
可以看到结果就没问题了
2. 通过pandas下的concat方法合并
举例:
>>> a = pd.DataFrame([[j for j in range(10)] for i in range(100)])
# 定义a和b两个DataFrame
>>> b = pd.DataFrame([[j*10 for j in range(10)] for i in range(100)])
>>> pd.concat([a.iloc[:, [1,2]], b[0]], axis=1)
# 在列的那一维度进行合并,可以看到结果没问题
1 2 0
0 1 2 0
1 1 2 0
2 1 2 0
3 1 2 0
4 1 2 0
.. .. .. ..
95 1 2 0
96 1 2 0
97 1 2 0
98 1 2 0
99 1 2 0
[100 rows x 3 columns]
这种方法是比较推荐的,一般来说使用都没问题。但有种情况下得谨慎使用:索引不匹配的时候。比如你在a中筛选了几行,然后和b中的其他几行进行合并,假如此时在a中你筛选出了所有的单数行,然后和b中的所有偶数行合并,那么可能就会出现如下情况:
>>> a_ = a.iloc[[i for i in range(1, 100, 2)], 0]
# 取a的所有奇数行
>>> a_
1 0
3 0
5 0
7 0
9 0
..
91 0
93 0
95 0
97 0
99 0
Name: 0, Length: 50, dtype: int64
>>> b_ = b.iloc[[i for i in range(0, 100, 2)], 0]
# 取b的所有偶数行
>>> b_
0 0
2 0
4 0
6 0
8 0
..
90 0
92 0
94 0
96 0
98 0
Name: 0, Length: 50, dtype: int64
>>> pd.concat([a_, b_], axis=1)
# 将两列合并
0 0
0 NaN 0.0
1 0.0 NaN
2 NaN 0.0
3 0.0 NaN
4 NaN 0.0
.. ... ...
95 0.0 NaN
96 NaN 0.0
97 0.0 NaN
98 NaN 0.0
99 0.0 NaN
[100 rows x 2 columns]
可以看到合并后的结果有问题,竟然有一对NaN
!究其原因,是因为两组合并的数据索引不同,所以相当于重新建立一个1到100的索引,然后两列对号入座,第一列就只有奇数行有数字,第二列只有偶数行有数字。所以如果我们只是想单纯的合并这两列的话,就需要把索引能够对上号,因此可以使用reset_index
方法重置索引后再合并,举例:
>>> a_
1 0
3 0
5 0
7 0
9 0
..
91 0
93 0
95 0
97 0
99 0
Name: 0, Length: 50, dtype: int64
>>> a_.reset_index()
# 重置索引,可以看到索引被重置成0到49了,并且还新建了一个`index`列来保留原来的索引
index 0
0 1 0
1 3 0
2 5 0
3 7 0
4 9 0
.. ... ..
45 91 0
46 93 0
47 95 0
48 97 0
49 99 0
[50 rows x 2 columns]
>>> a_.reset_index()[0]
# 这里我们不需要原来的索引,所以直接取第0列就可以了
0 0
1 0
2 0
3 0
4 0
..
45 0
46 0
47 0
48 0
49 0
Name: 0, Length: 50, dtype: int64
>>> pd.concat([a_.reset_index()[0], b_.reset_index()[0]], axis=1)
# 重置索引后合并,可以发现没问题了
0 0
0 0 0
1 0 0
2 0 0
3 0 0
4 0 0
.. .. ..
45 0 0
46 0 0
47 0 0
48 0 0
49 0 0
[50 rows x 2 columns]
然后我们尝试下用zip
方式合并索引没对上号的两列有没有问题:
>>> pd.DataFrame(list(zip(a_, b_)))
0 1
0 0 0
1 0 0
2 0 0
3 0 0
4 0 0
.. .. ..
45 0 0
46 0 0
47 0 0
48 0 0
49 0 0
[50 rows x 2 columns]
可以看到结果是没问题的,因为zip
只是对两列数据建立映射关系,并不关心索引。
由此可以得出结论:这两种方法的坑是互补的...哦不对...这两种方式使用时都有不同的坑,使用时务必要小心!