利用Python进行数据分析之pandas入门学习
文章目录
前言
本系列博文为利用 Python 进行数据分析相关工具包的学习,主要包含NumPy、pandas和matplotlib. 学习主要参考莫烦Python网站上面的教程。
一、pandas是什么?
NumPy 是将矩阵序列化,使用NumPy中避免了使用循环语句以达到某些计算目的。相比于NumPy,pandas则更像是“字典型的NumPy”,因为在pandas中我们可以给矩阵的行和列进行不同的命名。
总体来说,pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务而创建的,二者相互搭配使用。
二、pandas基本介绍
首先先引入pandas工具包,代码如下:
import numpy as np
import pandas as pd
2.1 创建pandas序列
s = pd.Series([1, 2, 3, 6, np.nan, 4]) #注意这里的Serious首字母要大写
s
# 0 1.0
# 1 2.0
# 2 3.0
# 3 6.0
# 4 NaN
# 5 4.0
# dtype: float64
上述代码中的np.nan为空值。注意在输入值的前侧有0-5的index,这有点像NumPy中的一维数据的索引。而dtype: float64则表示输入的数字格式为64位浮点型数值。
注:不同于NumPy中有int、float多种数值类型,Series中只有float一种数值类型。
2.2 创建DataFrame
矩阵在pandas中被称为DataFrame。首先需要定义一个date来作为描述其行的索引,如果不指定索引,则默认索引为0,1,2,…
dates = pd.date_range('20160101', periods=6)
dates
# DatetimeIndex(['2016-01-01', '2016-01-02', '2016-01-03', '2016-01-04',
# '2016-01-05', '2016-01-06'],
# dtype='datetime64[ns]', freq='D')
注意这里的dtype是日期(datatime[64])。
df = pd.(np.random.randn(6, 4), index=dates, columns=['a', 'b', 'c', 'd'])
df
# a b c d
# 2016-01-01 0.765527 -0.681561 -1.078267 -1.464479
# 2016-01-02 -1.022756 1.313707 0.158951 -0.261652
# 2016-01-03 -0.016420 0.463552 1.924633 1.851053
# 2016-01-04 -0.797636 -1.285860 -0.691840 1.170988
# 2016-01-05 0.306082 -0.242542 0.166530 1.747711
# 2016-01-06 -0.186562 0.959031 0.110047 0.666948
上述代码生成的DateFrame类似于NumPy中的矩阵,其中数据是使用NumPy中随机抽样所得,行索引(index)为之前定义的日期,列索引(columns)为[‘a’, ‘b’, ‘c’, ‘d’].
下面看一些不指定行索引和列索引情况下的输入。
df1 = pd.DataFrame(np.random.randn(12).reshape(3, -1))
df1
# 0 1 2 3
# 0 1.623124 -0.059781 -0.545961 1.278110
# 1 0.535882 -0.012219 1.429241 1.283243
# 2 -0.558577 0.288868 -1.380940 1.057342
这里的行列索引为默认的0,1,2,3.
2.3 DataFrame的基本属性
3.1 属性dtypes
dtypes表示DataFrame中每一列的数值属性
df1.dtypes
# 0 float64
# 1 float64
# 2 float64
# 3 float64
# dtype: object
3.2 索引
输出DataFrame中所有的索引。
df1.index # 输出所有行标签
# RangeIndex(start=0, stop=3, step=1)
df1.columns # 输出所有列标签
# RangeIndex(start=0, stop=4, step=1)
3.3 数值
输出所有数值(values)
df1.values
# array([[ 1.62312401, -0.05978129, -0.54596109, 1.2781104 ],
# [ 0.53588153, -0.01221874, 1.42924132, 1.28324285],
# [-0.5585765 , 0.28886821, -1.38094041, 1.05734184]])
3.4 描述
这里用describe()计算出一些统计量来描述每一列的一些信息。这里要注意describe()只针对数值类型的列进行计算,例如字符串类的列跳过不做分析。
df1.describe()
# 0 1 2 3
# count 3.000000 3.000000 3.000000 3.000000 #数据个数
# mean 0.533476 0.072289 -0.165887 1.206232 #数据均值
# std 1.090852 0.189064 1.443129 0.128968 #数据标准差
# min -0.558577 -0.059781 -1.380940 1.057342
# 25% -0.011347 -0.036000 -0.963451 1.167726
# 50% 0.535882 -0.012219 -0.545961 1.278110
# 75% 1.079503 0.138325 0.441640 1.280677
# max 1.623124 0.288868 1.429241 1.283243
3.5 转置
df1.T
# 0 1 2
# 0 1.623124 0.535882 -0.558577
# 1 -0.059781 -0.012219 0.288868
# 2 -0.545961 1.429241 -1.380940
# 3 1.278110 1.283243 1.057342
3.6 排序
df1.sort_index(axis=1, ascending=False)
上述代码中是根据列(index=1表示列,取0时表示行)名称进行排序,同时可以对ascending赋值True或False来确定是正向排序还是倒序。
不仅仅可以对行列索引进行排序,也可以对其中某个列的取值进行排序。
df1.sort_values(by=2) # 根据列索引为2的列中的值进行从小到大(默认)进行排序
三、pandas数据选择
首先生成一个DataFrame以供后面的学习。
import pandas as pd
import numpy as np
dates = pd.date_range('20160101', periods=6)
df = pd.DataFrame(np.arange(24).reshape((6, 4)), index=dates, columns=['a', 'b', 'c', 'd'])
df
# a b c d
# 2016-01-01 0 1 2 3
# 2016-01-02 4 5 6 7
# 2016-01-03 8 9 10 11
# 2016-01-04 12 13 14 15
# 2016-01-05 16 17 18 19
# 2016-01-06 20 21 22 23
3.1 输出指定列
print(df['a']) #或是df.a
上述两种方法都可以输出df的列索引为a的列。
3.2 输出指定行
选出指定行有两种方法,一个是直接给出行所在‘序号’,另一个是给出行的索引名称。
print(df[0:3]) #这里输出的[0:3]是左闭右闭的
# a b c d
# 2016-01-01 0 1 2 3
# 2016-01-02 4 5 6 7
# 2016-01-03 8 9 10 11
print(df['2016-01-02': '2016-01-04'])
# a b c d
# 2016-01-02 4 5 6 7
# 2016-01-03 8 9 10 11
# 2016-01-04 12 13 14 15
更一般地,可以用pandas的后缀‘loc’去描述选哪个标签下的数据,或是根据位置’position’选择:
# select by lobal:loc
print(df.loc['20160102'])
# a 4
# b 5
# c 6
# d 7
# Name: 2016-01-02 00:00:00, dtype: int32
print(df.loc['2016-01-04', ['a', 'b']])
# a 4
# b 5
# Name: 2016-01-02 00:00:00, dtype: int32
# select by position: iloc
print(df.iloc[3]) #输出第三行的数据
# a 12
# b 13
# c 14
# d 15
# Name: 2016-01-04 00:00:00, dtype: int32
print(df.iloc[3, 1]) #输出第三行第一位
# 13
print(df.iloc[1:3, 2:4]) #切片输出
# c d
# 2016-01-02 6 7
# 2016-01-03 10 11
print(df.iloc[[1,3,5], 2:4]) #逐个筛选后输出
# c d
# 2016-01-02 6 7
# 2016-01-04 14 15
# 2016-01-06 22 23
3.3 布尔逻辑选择
这里和NumPy中的布尔逻辑筛选很像。
#Boolean indexing
print(df[df.a > 8])
# a b c d
# 2016-01-04 12 13 14 15
# 2016-01-05 16 17 18 19
# 2016-01-06 20 21 22 23
print(df[[True, False, True, True, False, True]])
# a b c d
# 2016-01-01 0 1 2 3
# 2016-01-03 8 9 10 11
# 2016-01-04 12 13 14 15
# 2016-01-06 20 21 22 23
四、pandas设定值
4.1 使用位置参数设置值
这里可以使用上述的iloc、loc或布尔逻辑值取数后再设定值。
df.iloc[2, 2] = 111 # 将第2行第2列元素设置为111
df
# a b c d
# 2016-01-01 0 1 2 3
# 2016-01-02 4 5 6 7
# 2016-01-03 8 9 111 11
# 2016-01-04 12 13 14 15
# 2016-01-05 16 17 18 19
# 2016-01-06 20 21 22 23
df.loc['20160101', 'b'] = 222
df
# a b c d
# 2016-01-01 0 222 2 3
# 2016-01-02 4 5 6 7
# 2016-01-03 8 9 111 11
# 2016-01-04 12 13 14 15
# 2016-01-05 16 17 18 19
# 2016-01-06 20 21 22 23
df.b[df.a > 16] = 0
df
# a b c d
# 2016-01-01 0 222 2 3
# 2016-01-02 4 5 6 7
# 2016-01-03 8 9 111 11
# 2016-01-04 12 13 14 15
# 2016-01-05 16 17 18 19
# 2016-01-06 20 0 22 23
4.2 添加新的列
可以添加一列空值,然后再设定值。或是直接添加值。
df['e'] = np.nan
df
# a b c d e
# 2016-01-01 0 222 2 3 NaN
# 2016-01-02 4 5 6 7 NaN
# 2016-01-03 8 9 111 11 NaN
# 2016-01-04 12 13 14 15 NaN
# 2016-01-05 16 17 18 19 NaN
# 2016-01-06 20 0 22 23 NaN
df['f'] = pd.Series([1, 2, 3, 4, 5, 6], index=pd.date_range('20160101', periods=6))
df
# a b c d e f
# 2016-01-01 0 222 2 3 NaN 1
# 2016-01-02 4 5 6 7 NaN 2
# 2016-01-03 8 9 111 11 NaN 3
# 2016-01-04 12 13 14 15 NaN 4
# 2016-01-05 16 17 18 19 NaN 5
# 2016-01-06 20 0 22 23 NaN 6
上述代码中的第二部分可以参考第3节,首先生成取值为1到6的序列,然后将其行标签设定为和df相同的时间序列,一一对应。
五、处理丢失值
首先生成一个DataFrame以供后面的学习分析。
import pandas as pd
import numpy as np
dates = pd.date_range('20160101', periods=6)
df = pd.DataFrame(np.arange(24).reshape((6, 4)), index=dates, columns=['a', 'b', 'c', 'd'])
df.iloc[0, 1] = np.nan
df.iloc[1, 2] = np.nan
df
# a b c d
# 2016-01-01 0 NaN 2.0 3
# 2016-01-02 4 5.0 NaN 7
# 2016-01-03 8 9.0 10.0 11
# 2016-01-04 12 13.0 14.0 15
# 2016-01-05 16 17.0 18.0 19
# 2016-01-06 20 21.0 22.0 23
很多时候DataFrame中都含有丢失值(NaN),首先要用isnull函数检查是否有缺失值,返回的是由True和False组成的DataFrame:
print(df.isnull())
还可以用any函数判断返回的DataFrame中是否含有True:
print(np.any(df.isnull()) == True)
# True
下面介绍如何处理这些丢失值。
5.1 将含有缺失值的行或列丢掉
print(df.dropna(axis=0, how='any')) # how='all'
上述代码中,axis取0/1时表示将含有缺失值的行/列丢掉,how取any时表示含有一个缺值值就丢掉,取all时表示全部值都是空值时丢掉,默认how取any。
5.2 将缺失值设定为某一指定数值(填数)
这里可以使用fillnan函数将value的值填到空值处。
print(df.fillna(value=0))
六、数据的读取与保存
首先说明一下pandas可以读取的数据格式,主要包含:csv、excel、hdf、sql、json、msgpack、html、gbd、stata、sas、clipborad、pickle。命令read_加上上述格式即为读取相应格式的命令,to_加上上述格式即将数据保存为相应格式的命令。
注:通常在处理数据时推荐使用csv格式,因为其存储方式简单。
6.1 数据的读取
import pandas as pd
data = pd.read_csv(r'C:\Users\HUAWEI\Desktop\student.csv')
print(data)
# Student_id name age gender
# 0 1100 A 22 Female
# 1 1101 B 21 Male
# 2 1102 C 25 Female
# 3 1103 D 24 Male
# 4 1104 E 23 Female
# 5 1105 F 20 Male
# 6 1106 G 23 Female
# 7 1107 H 24 Male
# 8 1108 I 21 Female
# 9 1109 G 22 Male
6.2 数据的保存
将上述data中的数据保存为xlsx格式
data.to_excel(r'C:\Users\HUAWEI\Desktop\student.xlsx')
七、DataFrame的合并
合并可以横向合并、纵向合并,或是按照row、column索引合并,即使行列索引不同仍然可以合并。
7.1 DataFrame的合并之concat
首先还是生成DataFrame:
import numpy as np
import pandas as pd
# concatenating
df0 = pd.DataFrame(np.ones((3,4))*0, columns=['a','b','c','d'])
df1 = pd.DataFrame(np.ones((3,4))*1, columns=['a','b','c','d'])
df2 = pd.DataFrame(np.ones((3,4))*2, columns=['a','b','c','d'])
1. 上下合并
下面将三个含有相同的colume的DataFrame进行合并,在concat函数中,参数axis取0是表示竖向合并,取1时表示横向合并.
result = pd.concat([df0, df1, df2], axis = 0)
print(result)
# a b c d
# 0 0.0 0.0 0.0 0.0
# 1 0.0 0.0 0.0 0.0
# 2 0.0 0.0 0.0 0.0
# 0 1.0 1.0 1.0 1.0
# 1 1.0 1.0 1.0 1.0
# 2 1.0 1.0 1.0 1.0
# 0 2.0 2.0 2.0 2.0
# 1 2.0 2.0 2.0 2.0
# 2 2.0 2.0 2.0 2.0
可以看出,上述将三个DataFrame合并后的行索引是之前的三个DataFrame(0,1,2),这里可以通过对参数ignore_index赋值进行重新排序:
result = pd.concat([df0, df1, df2], axis = 0, ignore_index=True)
print(result)
# a b c d
# 0 0.0 0.0 0.0 0.0
# 1 0.0 0.0 0.0 0.0
# 2 0.0 0.0 0.0 0.0
# 3 1.0 1.0 1.0 1.0
# 4 1.0 1.0 1.0 1.0
# 5 1.0 1.0 1.0 1.0
# 6 2.0 2.0 2.0 2.0
# 7 2.0 2.0 2.0 2.0
# 8 2.0 2.0 2.0 2.0
concat中还有一个参数是join,类似于axis和ignore_index,其取值范围为{‘inner’,‘outer’}.(这里和SQL的内外连接很像,即inner取交集,outer取并集,可以参考)
由于此处主要讨论根据行列索引进行连结,所以需要重新生成几个DataFrame以供学习:
# join,['inner','outer']
df0 = pd.DataFrame(np.ones((3,4))*0, columns=['a','b','c','d'],index=[1,2,3])
df1 = pd.DataFrame(np.ones((3,4))*1, columns=['a','b','c','e'],index=[2,3,4])
注意生成的两个DataFrame的column和index不全相同。
首先看一下直接使用上述提到的concat进行连接会出现什么结果:
result = pd.concat([df0, df1]) # join='outer'
print(result)
# a b c d e
# 1 0.0 0.0 0.0 0.0 NaN
# 2 0.0 0.0 0.0 0.0 NaN
# 3 0.0 0.0 0.0 0.0 NaN
# 2 1.0 1.0 1.0 NaN 1.0
# 3 1.0 1.0 1.0 NaN 1.0
# 4 1.0 1.0 1.0 NaN 1.0
直接使用concat会导致两个DataFrame不同的地方直接为空值NaN。实际上这里concat函数中的参数join取缺省值‘outer’。如果参数join取inner会导致不同的部分直接被舍弃掉。
result = pd.concat([df0, df1], join='inner')
print(result)
# a b c
# 1 0.0 0.0 0.0
# 2 0.0 0.0 0.0
# 3 0.0 0.0 0.0
# 2 1.0 1.0 1.0
# 3 1.0 1.0 1.0
# 4 1.0 1.0 1.0
7.2 左右合并
result = pd.concat([df0, df1.reindex_like(df0)], axis = 1)
print(result)
# a b c d a b c d
# 1 0.0 0.0 0.0 0.0 NaN NaN NaN NaN
# 2 0.0 0.0 0.0 0.0 1.0 1.0 1.0 NaN
# 3 0.0 0.0 0.0 0.0 1.0 1.0 1.0 NaN
首先将axis取值为1表示横向合并,同时使用df1.reindex_like(df0)表示保留df0的全部行列,将df1中与df0行列标签相同的元素保留,行列都不同的直接删除,行不同或列不同的取值为空NaN。
7.2 DataFrame的合并之append
想想一张excel表格,每一列都是一个属性,每一行为一个对象。append函数的运行过程就像是在excel中添加对象的过程。下面看一个例子感受一下:
#append
df0 = pd.DataFrame(np.ones((3,4))*0, columns=['a','b','c','d'])
df1 = pd.DataFrame(np.ones((3,4))*1, columns=['a','b','c','d'])
df2 = pd.DataFrame(np.ones((3,4))*1, columns=['a','b','c','e'],index=[2,3,4])
result0 = df0.append(df1, ignore_index=True)
print(result0)
# a b c d
# 0 0.0 0.0 0.0 0.0
# 1 0.0 0.0 0.0 0.0
# 2 0.0 0.0 0.0 0.0
# 3 1.0 1.0 1.0 1.0
# 4 1.0 1.0 1.0 1.0
# 5 1.0 1.0 1.0 1.0
result1 = df0.append([df1, df2])
上述result1表示将df1,df2合并到df0下侧。接下来看一下如何添加单独一行:
s1 = pd.Series([1, 2, 3, 4], index=['a','b','c','d'])
result2 = df0.append(s1, ignore_index=True)
print(result2)
# a b c d
# 0 0.0 0.0 0.0 0.0
# 1 0.0 0.0 0.0 0.0
# 2 0.0 0.0 0.0 0.0
# 3 1.0 2.0 3.0 4.0
7.3 DataFrame的合并之merge
相对于concat,merge功能更加全面,当然也更加复杂,下面来学习一下merge。
首先生成数据:
import numpy as np
import pandas as pd
# merging two df by key/keys. may be used in database.
# simple example
left = pd.DataFrame({'key':['K0','K1','K2','K3'],
'A':['A0','A1','A2','A3'],
'B':['B0','B1','B2','B3']})
right = pd.DataFrame({'key':['K0','K1','K2','K3'],
'C':['C0','C1','C2','C3'],
'D':['D0','D1','D2','D3']})
print(left)
# key A B
# 0 K0 A0 B0
# 1 K1 A1 B1
# 2 K2 A2 B2
# 3 K3 A3 B3
print(right)
# key C D
# 0 K0 C0 D0
# 1 K1 C1 D1
# 2 K2 C2 D2
# 3 K3 C3 D3
然后来基于key这一列进行合并:
res = pd.merge(left, right, on = 'key')
print(res)
# key A B C D
# 0 K0 A0 B0 C0 D0
# 1 K1 A1 B1 C1 D1
# 2 K2 A2 B2 C2 D2
# 3 K3 A3 B3 C3 D3
之后考虑根据两列来进行合并:
#consider two keys
left = pd.DataFrame({'key1':['K0','K0','K1','K2'],
'key2':['K0','K1','K0','K1'],
'A':['A0','A1','A2','A3'],
'B':['B0','B1','B2','B3']})
right = pd.DataFrame({'key1':['K0','K1','K1','K2'],
'key2':['K0','K0','K0','K0'],
'C':['C0','C1','C2','C3'],
'D':['D0','D1','D2','D3']})
res = pd.merge(left, right, on=['key1','key2'])
# how = ['left','right','inner','outer']
print(res)
# key1 key2 A B C D
# 0 K0 K0 A0 B0 C0 D0
# 1 K1 K0 A2 B2 C1 D1
# 2 K1 K0 A2 B2 C2 D2
这里需要特殊注意的是,和concat函数中join弄人为outer不同,merge的连接方式默认为inner,即交集。在上述代码中表示只有当两个DataFrame的key1列和key2列的取值完全相同时才会连接到一起,只有由一个不同即丢弃。
merge函数中的参数how(如何连接)有四个可取值,分别为’left’,‘right’,‘inner’和’outer’,后两个就不再赘述了,而left则表示按照左侧的(前一个)DataFrame的行标签进行连接,right表示按照右侧(后一个)DataFrame的行标签进行连接。
res1 = pd.merge(left, right, on=['key1','key2'], how='left', indicator=True)
print(res1)
# key1 key2 A B C D _merge
# 0 K0 K0 A0 B0 C0 D0 both
# 1 K0 K1 A1 B1 NaN NaN left_only
# 2 K1 K0 A2 B2 C1 D1 both
# 3 K1 K0 A2 B2 C2 D2 both
# 4 K2 K1 A3 B3 NaN NaN left_only
上述代码中,函数merge中加入的参数indicator,当其取值为True时,可以在输出的表格的最后一列展示每行标签是both,left_only还是right_only。此时该列名称默认为_merge,可直接给参数indicator传递一个字符串以作为该列名称:
res1 = pd.merge(left, right, on=['key1','key2'], how='left', indicator='indicator_column')
学习完根据一列或是两列进行连接后,我们再来看一下根据行索引进行连接:
left = pd.DataFrame({'A':['A0','A1','A2','A3'],
'B':['B0','B1','B2','B3']},
index=['K0','K1','K2','K3'])
right = pd.DataFrame({'C':['C0','C1','C2','C3'],
'D':['D0','D1','D2','D3']},
index=['K0','K1','K4','K3'])
res = pd.merge(left, right, left_index=True, right_index=True, how='inner')
print(res)
# A B C D
# K0 A0 B0 C0 D0
# K1 A1 B1 C1 D1
# K3 A3 B3 C3 D3
根据行索引进行连接的参数取值和根据列进行连接一样,就不再赘述了。
最后来考虑一种情况:假设我们想要根据A这一列来连接两个DataFrame,但是我们想在连接后新的DataFrame中将两个原始DataFrame的列区分开来,需进行如下操作:
boys = pd.DataFrame({'k':['k0','k1','k2'], 'age':[1,2,3]})
girls = pd.DataFrame({'k':['k0','k2','k3'], 'age':[4,5,6]})
# 我们想要在新的DataFrame中将两个age区分开来
res = pd.merge(boys, girls, on='k', suffixes=['_boy','_girl'], how='inner')
print(res)
# k age_boy age_girl
# 0 k0 1 4
# 1 k2 3 5
上述代码中,通过对参数suffixes赋值,这使得得到的新的DataFrame中boys的每个列标签后面都加上了_boy,girls的每个列标签后面都加上了_girl。
八、plot画图
这里调用matplotlib包进行画图:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
常见的画图方法主要包含:bar、hist、box、kde、area、scatter、hexbin等等,在此不详细展开,但是在之后的matplotlib的学习中会系统介绍画图的方式方法。
下面看几个简单的用plot画图的例子:
【例1】:线性数据
# plot data
#Series 线性数据
data = pd.Series(np.random.randn(1000), index=np.arange(1000))
data = data.cumsum()
data.plot()
plt.show()
【例2】:DataFrame
# DataFrame
data = pd.DataFrame(np.random.randn(1000,4), index=np.arange(1000), columns=list('abcd'))
data = data.cumsum()
print(data.head())
# a b c d
# 0 0.850435 -0.779919 1.576871 0.760385
# 1 0.547593 0.128119 0.304691 0.737124
# 2 -0.334124 0.111012 0.526692 0.300202
# 3 0.007890 -0.099115 -1.474958 -0.074081
# 4 -2.316933 -1.993305 -3.500215 -1.471161
data.plot()
plt.show()
接下来看画散点图的例子:
ax = data.plot.scatter(x='a',y='b',color='DarkBlue',label='Class1')
data.plot.scatter(x='a', y='c', color='DarkGreen', label='Class2', ax=ax)
plt.show()
上述代码第二行中的ax=ax,前一个ax表示改行的图,后一个ax表示上一个图,令二者相等表示将第二个图画到第一个图中去。
总结
以上就是pandas基础学习的内容,本文仅仅简单介绍了pandas基本使用方法,其更多的功能还需要在实践中慢慢探索学习,各位加油。祝我们变得更强!
注1:建议搭配pandas官方学习手册一起学习。
注2:有不足或错误之处欢迎各位及时指出,感谢理解。