在python中,Pandas可以说是最实用的库之一,它提供了非常丰富的数据读写方法。可以看一下Pandas中文网提供的Pandas参考文档中对所有I/O函数的总结。
Pandas是一个开源的,BSD许可的库,为python提供高性能、易于使用的数据结构和数据分析工具。它的使用基础是Numpy(提供高性能的矩阵运算);可以用于数据挖掘和数据分析,同时也提供数据清洗的功能。
本文就对Pandas的基本使用做一个简单的归纳,所有代码可以从上往下按顺序依次执行。
References:
电子文献:
https://www.cnblogs.com/chenhuabin/p/11477076.html
https://blog.csdn.net/weixin_39791387/article/details/81487549
https://kanoki.org/2019/09/16/dataframe-visualization-with-pandas-plot/
https://pandas.pydata.org/pandas-docs/stable/getting_started/10min.html
https://blog.csdn.net/weixin_41712499/article/details/82719987
csv
这里我想介绍一下一种新的数据格式:csv。它和excel很像,但又不同于excel。csv主要有如下特点:
- 纯文本,使用某个字符集,比如ASCII、Unicode、EBCDIC或GB2312(简体中文环境)等;
- 由记录组成(典型的是每行一条记录);
- 每条记录被分隔符(英语:Delimiter)分隔为字段(英语:Field (computer science))(典型分隔符有逗号、分号或制表符;有时分隔符可以包括可选的空格);
- 每条记录都有同样的字段序列。
在Pandas的使用以及AI相关竞赛数据集、结果的存储与使用中,csv文件往往承担着主角的位置。
准备
在具体使用之前,别忘了先导入所需相应的库。
1 | import pandas as pd |
可以使用pd.__version__
来输出版本号,注意,这里的“__”是两个“_”,这个很容易搞错且难以发现。
两大利器
Pandas中文网首页,在介绍完Pandas之后,就重点介绍了一下Pandas的两大利器。分别是DataFrame和Series。这里我先介绍一下Seires,DataFrame在后面有更详细的操作。
Series简介
Series是一种类似于一维数组的对象,是由一组数据(各种NumPy数据类型)以及一组与之相关的数据标签(即索引)组成。仅由一组数据也可产生简单的Series对象。
我们可以通过传入一个list的数值来创建一个Series,Pandas会创建一个默认的整数索引:1
2
3
4
5
6
7
8
9
10s = pd.Series([1, 3, 5, np.nan, 6, 8])
s
0 1.0
1 3.0
2 5.0
3 NaN
4 6.0
5 8.0
dtype: float64注:这里用
np.nan
来产生NaN,但要注意的是np.nan
不是一个“空”对象,即使用np.nan == np.nan
来判断将返回False,np.nan
的类型为基本数据类型float。
若要对某个值进行空值判断,如对np.nan
,需要用np.isnan(np.nan)
,此时返回为True。另外,也可以从字典创建Series。
DataFrame简介
DataFrame是Pandas中的一个表格型的数据结构,包含有一组有序的列,每列可以是不同的值类型(数值、字符串、布尔型等),DataFrame即有行索引也有列索引,可以被看做是由Series组成的字典。
我们可以通过传入一个numpy数组来创建一个DataFrame,如下面带有一个datetime的索引以及被标注的列:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17dates = pd.date_range('20130101', periods = 6)
dates
'2013-01-01', '2013-01-02', '2013-01-03', '2013-01-04', DatetimeIndex([
'2013-01-05', '2013-01-06'],
dtype = 'datetime64[ns]', freq = 'D')
df1 = pd.DataFrame(np.random.randn(6, 4), index = dates, columns = list('ABCD'))
df1
A B C D
2013-01-01 0.469112 -0.282863 -1.509059 -1.135632
2013-01-02 1.212112 -0.173215 0.119209 -1.044236
2013-01-03 -0.861849 -2.104569 -0.494929 1.071804
2013-01-04 0.721555 -0.706771 -1.039575 0.271860
2013-01-05 -0.424972 0.567020 0.276232 -1.087401
2013-01-06 -0.673690 0.113648 -1.478427 0.524988注:上面用
pd.data_range()
生成了一个时间频率freq = 'D'
(即天)的日期序列。我们也可以通过传入一个可以转换为类Series(series-like)的字典对象来创建一个DataFrame:
1
2
3
4
5
6
7
8
9
10
11
12
13df2 = pd.DataFrame({'A': 1.,
'B': pd.Timestamp('20130102'),
'C': pd.Series(1, index = list(range(4)), dtype = 'float32'),
'D': np.array([3] * 4, dtype = 'int32'),
'E': pd.Categorical(["test", "train", "test", "train"]),
'F': 'foo'})
df2
A B C D E F
0 1.0 2013-01-02 1.0 3 test foo
1 1.0 2013-01-02 1.0 3 train foo
2 1.0 2013-01-02 1.0 3 test foo
3 1.0 2013-01-02 1.0 3 train foo这里可以使用
df2.dtypes
来查看不同列的数据类型。
读取
无论是txt文件还是csv文件,在Pandas中都使用read_csv()
读取,当然也使用同一个方法写入到文件,那就是to_csv()
方法。
1 | df = pd.read_csv(abs_path) #此为绝对路径 |
为了提供更加多样化、可定制的功能,read_csv()方法定义了数十个参数,还在大部分参数并不常用,以下是几个比较常用的参数:
- filepath_or_buffer:文件所在路径,可以是一个描述路径的字符串、pathlib.Path对象、http或ftp的连接,也可以是任何可调用
read()
的对象。这是唯一一个必传的参数,也就是上面的abs_path。 - encoding:编码,字符型,通常为utf-8,如果中文读取不正常,可以将encoding设为gbk。当然,也可以直接将对应文件改成utf-8编码。
- header:整数或者由整数组成的列表,用来指定由哪一行或者哪几行作为列名,默认为
header = 0
,表示用第一列作为列名。若设置header = 0
,则指定第二列作为列名。要注意的是,当指定第一行之后的数据作为列名时,前面的所有行都会被略过。也可以传递一个包含多个整数的列表给header,这样每一列就会有多个列名。如果中间某一行没有指定,那么该行会被略过。例如header = [0, 2]
,则原本的第二行会被省去。而当文件中没有列名一行数据时,可以传递header = None
,表示不从文件数据中指定行作为列名,这时Pandas会自动生成从零开始的序列作为列名。 - names:接着上面的header,很快就想到是不是可以自己设置列名。names就可以用来生成一个列表,为数据额外指定列名。例如:
df = pd.read_csv('abs_path, names=['第一列', '第二列', '第三列', '第四列'])
。
在数据读取完毕之后,我们可以使用如下代码来快速查看数据是否正确地导入了。
1 | df.head() #看一下导入后df(DataFrame)的前几行,可在括号内输入数字来设定具体显示几行,默认5行 |
DataFrame
DataFrame的介绍在前面的简介已经写过,这里就不赘述了。事实上,Pandas中的DataFrame的操作,有很大一部分跟numpy中的二维数组的操作是近似的。
在上面的读取处理之后,我们下面对其进行一些简单的操作:
查看
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18df.head() #上文已提及
df.tail()
#查看列名
print(df.columns)
#查看索引
print(df.index)
#查看各列的数据格式
print(df.dtypes)
#查看整个DataFrame的属性信息
print(df.info())
#访问对应行
df.loc[0] #这里访问了第一行,将显示列名和对应每一列第一行的数据
#具体有关索引请看后文筛选
在numpy中,我们可以这样判断一个数组中每一个数和对应数值的比较结果:
1
2a = np.array(range(10))
a > 3输出将是一串布尔型(True、False)的array。
而在DataFrame中,我们可以用类似的方法通过指定列来进行筛选:1
2#筛选第二列中数值大于80
df[df.第二列 > 80]这样就会得到只用符合条件数据的对应行的一个DataFrame。
我们也可以使用df[(df.第一列 > 80) & (df.第二列 > 80) & (df.第三列 > 80)]
来进行多条件的复杂筛选。
此外,我们可以直接根据列名提取出一个新的表格:1
new = df[['第一列', '第二列']] #new为仅由第一列和第二列组成的一个新的DataFrame
排序
可以使用如下代码根据单列或者多列的值对数据进行排序:
1
2df.sort_values(['第二列', '第一列', '第三列'], ascending = [True, True, True])
#使用df.sort_values(['第二列', '第一列', '第三列']).head()查看排序完后前几行的结果这里排序的规则是:根据设置的顺序(这里是先按第二列排),从小到大升序对所有数据进行排序。其中ascending是设置升序(默认True)和降序(False)。若仅选择单列,则无需添加
[]
,这里[]
的作用是把选择的行列转换为列表。重命名
如果觉得我前面取得列名称不好听,可以使用下面这个代码来改成需要的名字:
1
df.rename(columns = {'第一列': '好听的第一列', '第二列': '好听的第二列', '第三列': '好听的第三列', '第四列': '好听的第四列',}, inplace = True)
这里用到了字典。
索引
前面提到了使用索引来查看第一行,可当没有数字索引,例如我们通过
df = pd.DataFrame(scores, index = ['one', 'two', 'three'])
把index设为one、two、three时,df.loc[0]
就失效了。因此有下面几种处理方法:1
2
3
4
5
6
7
8
9#访问index为“one”的行
df.loc['one']
#访问实实在在所谓的第几行(无论index为何)
df.iloc[0] #注意0指的是第一行
#ix合并了loc和iloc的功能,当索引为数字索引的时候,ix和loc是等价的
df.ix[0] #访问第一行
df.ix['one'] #访问“one”行,这里也指的是第一行切片
类似的,DataFrame也支持切片操作,但还是需要注意的。
这里总结两种切片方式:插入
上面的索引还有一种用途,就是可以用于插入指定index的新行。
1
df.loc['new_index'] = ['one', 'piece', 'is', 'true']
删除
上面插入的那行中我说了“大秘宝是真实存在的”(海贼迷懂),下面我想把这句话所在的行删了,可以使用
df.dtop()
来完成。1
df = df.drop('new_index')
数组
我们可以使用
df.第一列.values
以array的形式输出指定列的所用值。
基于此,我们可以使用df.第一列.value_counts()
来做简单的统计,也就是对该列中每一个出现数字作频次的统计。
我们还可以直接对DataFrame做计算,例如:df * n
(n为具体数值),结果就是对表中的每一个数值都乘上对应的倍数。元素操作
map函数
map()是python自带的方法, 可以对DataFrame某列内的元素进行操作。
下面是一种使用实例:1
2
3
4
5
6
7
8
9
10
11def func(grade):
if grade >= 80:
return "A"
elif grade >= 70:
return "B"
elif grade >= 60:
return "C"
else:
return "D"
df['评级'] = df.第一列.map(func)这样DataFrame后面会自动添加一列名为“评级”,并根据第一列来生成数据填入。
apply函数
当我们需要进行根据多列生成新的一个列的操作时,就需要用到apply。其用法简单示例如下:
1
df['求和'] = df.apply(lambda x: x.第一列 + x.第二列, axis = 1)
applymap函数
applymap时对dataframe中所有的数据进行操作的一个函数,非常重要。例如,我要让之前所用的score和grade都变成scroe+或者grade+,那么我就可以这样:
1
df.applymap(lambda x: str(x) + '+')
如果是成绩单的话,那么这样操作之后打出来就会好看些啦,哈哈。
plot
数据可视化本来是一个非常复杂的过程,但Pandas数据帧plot函数的出现,使得创建可视化图形变得很容易。
这个函数的具体使用可以访问文首给出的第三个参考链接,为一个印度小哥利用kaggle上的数据对df.plot()
做的一个非常详尽的介绍。
有机会的话我会结合matplotlib对其做一个搬运与总结,先留个坑。统计
我们可以使用df.describe()
对数据进行快速统计汇总。输出将包括:count、mean、std、min、25%、50%、75%和max。
通过df.mean()
我们可以按列求均值,如果想要按行求均值,可以使用df.mean(1)
。高阶
此外还有使用df.T
进行转置,df.dropna(how = 'any')
删除所有具有缺失值的数据,df.fillna(value = 5)
填充所有缺失数据等高阶用法。详细的可以查阅前面给的参考链接10 minutes to pandas,至于更高阶的,可以看一下cookbook,不过一般还是在运用的过程中遇到需求再查找,一下子记不住那么多的。
写入
通过to_csv()
可以将Pandas数据写入到文本文件中,和读取read_csv()
类似,它也有几个常用参数:
- path_or_buf:表示路径的字符串或者文件句柄,也是必需的。例如:
df.to_csv(abs_path)
。要注意的是,这里如果abs_path对应的文件不存在,则会新建abs_path的同名文件后再写入,如果本来已存在该文件,则会自动清空该文件后再写入。 - sep:分隔符,默认为逗号。当写入txt文件时,就需要这个参数来确定数据之间的分隔符了。
- header:元素为字符串的列表或布尔型数据。当为列表时表示重新指定列名,当为布尔型时,表示是否写入列名。这和读取时的使用基本类似。
- columns:后接一个列表,用于重新指定写入文件中列的顺序。例如:
df.to_csv(abs_path, columns = ['第四列', '第二列', '第三列', '第一列'])
。 - index_label:字符串或布尔型变量,设置索引列列名。原本的索引是空的,使用这个参数就可以给索引添加一个列名。如果觉得不需要添加,同时空着不好看(空的话还是会有分隔符),可以设置为False去掉(同时也将不显示分隔符)。
- index:布尔型,是否写入索引列,默认为True。
- encoding:写入时所用的编码,默认是utf-8。这个和上述的许多参数其实保持默认即可。
匿名函数
在上面DataFrame一节的最后,我用到了两个匿名函数,这里我想举个例子来简单展示一下匿名函数的使用方法,的确很好用!
当我们对一个数进行操作时,若使用函数,一般会:
1 | def func(number): |
这样看上去就有点费代码了,因此有下面的等价匿名函数可以替代:
1 | func = lambda number: number + 10 |
当然假如想追求代码行数的话也不拦着你~
推荐
最近看到了DataWhale的一篇文章,也总结的挺好,在这里推荐一下。