近年来,随着数据科学的逐步发展,Python语言的使用率也越来越高,不仅可以做数据处理,网页开发,更是数据科学、机器学习、深度学习等从业者的首选语言。
TIOBE Index for October 2023 “工欲善其事,必先利其器。” 要做好数据分析,离不开一个好的编程工具,不论是从Python的语法之简洁、开发之高效,招聘岗位之热门来说,Python都是数据科学从业者需要掌握的一门语言。
但一直以来,人们却误以为“学会Python”是件很难的事情。
实则不然,这恰恰是我们选择学Python的理由之一。
事实证明,Python人人皆能学会——千万别不信。
老少皆宜 —— 也就是说,“只要你愿意”,根本没有年龄差异。十二岁的孩子可以学;十八岁的大学生可以学;在职工作人员可以学…… 就算你已经退休了,想学就能学,谁也拦不住你。
不分性别,男性可以学,女性同样可以学,性别差异在这里完全不存在。
不分国界,更没有区域差异 —— 互联网的恩惠在于,你在北京、纽约也好,老虎沟、门头沟也罢,在这个领域里同样完全没有任何具体差异。
尤其是在中国。现状是,中国的人口密度极高,优质教育资源的确就是稀缺…… 但在计算机科学领域,所有的所谓 “优质教育资源” 事实上完全没有任何独特的竞争力 —— 编程领域,实际上是当今世上极为罕见的 “教育机会公平之地”。又不仅在中国如此,事实上,在全球范围内也都是如此。
多年以前,不识字的人被称为文盲……
后来,不识英文,也是文盲。人们发现很多科学文献的主导语言都是英语……
再后来,不会计算机的也算是文盲,因为不会计算机基本操作,很多工作的效率很低下,浪费了很多时间……
近些年,不会数据分析的,也算做文盲了,互联网高速发展,用户行为数据越来越多,
你作为一个个体,每天都在产生各种各样的数据,然后时时刻刻都在被别人使用着、分析着…… 然而你自己却全然没有数据分析能力,甚至不知道这事很重要,是不是感觉很可怕?你看看周边那么多人,有多大的比例想过这事?反正那些天天看机器算法生成的信息流的人好像就是全然不在意自己正在被支配……
怎么办?学呗,学点编程罢 —— 巧了,这还真是个正常人都能学会的技能。
为便于上手学习,在开始前再做两点补充
众所周知,在数据分析相关的编程语言中,有三个重中之重: Python、R、Julia 俗称数据科学三剑客。如果有一个工具能集中编写这三者的代码,为大家省去各种安装开发工具的时间,那简直太好不过了,于是jupyter应运而生,作为“工具的工具”而备受数据科学从业者的青睐!
Jupyter 的名字就很好地诠释了这一发集大成的思路,它是 Julia、Python、R 语言的组合,拼写相近于木星(Jupiter),而且现在支持的语言也远超这三种了。
所以需要读者自行 下载安装好Anaconda提供的Jupyter notebook或者jupyter lab ,以便于更好地运行本文相关代码。安装好后可以直接运行Python,因为里面已经帮你集成好了环境。
虽然笔者力求极简,带你入门Python,但你依然有可能遇到问题,因为编程语言的知识体系有一个特点,知识点之间不是线性的从前往后,而是呈网状的,经常出现前置引用。所以很多时候可能不经意间就用的是后面的知识点,这是不可避免的,遇到这种情况,注定要 反复阅读 若干遍,之所以取名叫极简入门,这一部分的目标就在与并不是让你立马学会就去写代码,而是让你脱盲……
好了,接下来,与大家一起开始我们的Python旋风之旅,沿用之前《极简统计学入门》、《SQL数据分析极简入门》的“MVP”思路,用三节的内容梳理一下Python数据分析的核心内容。整个系列框架如下:
第1节 Python基础知识 如果你接触过不同编程语言就会发现,任何编程语言的学习,都离不开3个最基本的核心要素, 数据类型、流程控制、函数
数据类型 是用来描述数据的性质和特征的,它决定了数据在计算和处理过程中的行为和规则。常见的数据类型包括整数、浮点数、字符串、日期等。简而言之,就是 你将要操作的东西具有什么样的特点 。
流程控制 是指通过条件判断和循环等方式,控制程序按照一定的顺序执行不同的操作步骤。它决定了数据的处理流程,包括判断条件、循环次数、分支选择等。简而言之,就是 你要操作这个东西的基本流程是什么 。
函数 是一段预先定义好的代码,用于执行特定的操作或计算。它接受输入参数,并返回一个结果。函数可以用来对数据进行各种计算、转换、筛选等操作,以满足特定的需求。简而言之,就是 你要怎么样才能可复用地操作这一类东西 。
我们来看第一个核心要素:数据类型
数据类型 Python中的数据类型有很多,如果我们按照大类来分,可以分为三大数据类型:
① 数字型 整型 int
a = 2022 # 把2022赋值给a type(a) # 查看数据类型:
int
浮点型 float
b = -21.9 type(b) # 数据类型:
float
复数型 complex
c =
11 + 36j type(c) # 数据类型:
complex
布尔型 bool
d = True type(d) # 数据类型:
bool
② 字符串型 str_a = "Certified_Data_Analyst" # 创建字符串:"Certified_Data_Analyst" type(str_a) # 数据类型:
str
len(str_a) # 字符串长度:
22
str_a[ 0 ] # 输出字符串第1个字符
'C'
str_a[ 10 ] # 输出字符串第11个字符
'D'
str_a[ 15 ] # 输出字符串第16个字符
'A'
str_a[: 9 ] # 输出字符串第1到9个字符
'Certified'
str_a[ 10 : 14 ] # 输出从第11到14个的字符
'Data'
str_a[ 15 :] # 输出从第15个后的所有字符
'Analyst'
str_a * 2 # 输出字符串2次
'Certified_Data_AnalystCertified_Data_Analyst'
str_a+ "_Exam" # 连接字符串
'Certified_Data_Analyst_Exam'
str_a.upper() # 转换为大写
'CERTIFIED_DATA_ANALYST'
str_a.lower() # 转换为小写
'certified_data_analyst'
int( "1024" ) # 字符串转数字:1024
1024
str( 3.14 ) # 数字转字符串:'3.14'
'3.14'
"Certified_Data_Analyst" .split( "_" ) # 分隔符拆分字符串
['Certified', 'Data', 'Analyst']
"Certified_Data_Analyst" .replace( "_" , " " ) # 替换字符串"_"为" "
'Certified Data Analyst'
"7" .zfill( 3 ) # 左边补0
'007'
③ 容器型 可以容纳多个元素的的对象叫做容器,这个概念比较抽象,你可以这么理解,容器用来存放不同的元素,根据存放特点的不同,常见的容器有以下几种:list列表对象、tuple列表对象、dict列表对象、set集合对象
列表 list() 用中括号 []
可以创建一个list变量
[ 2 , 3 , 5 , 7 ]
[2, 3, 5, 7]
元组 tuple() 用圆括号 ()
可以创建一个tuple变量
( 2 , 3 , 5 , 7 )
(2, 3, 5, 7)
集合 set() 用花括号 {}
可以创建一个集合变量
{ 2 , 3 , 5 , 7 }
{2, 3, 5, 7}
字典 dict() 用花括号 {}
和冒号 :
,可以创建一个字典变量
{ 'a' : 2 , 'b' : 3 , 'c' : 5 , 'd' : 7 }
{'a': 2, 'b': 3, 'c': 5, 'd': 7}
流程控制 分支
举例说明,我们给 x
赋值-10,然后通过一个分支做判断,当x大于零时候输出"x是正数",当x小于零的时候输出"x是负数",其他情况下输出"x是零"
x = -10 if x > 0 : print( "x是正数" ) elif x 0 : print( "x是负数" ) else : print( "x是零" )
x是负数
循环
举例说明for循环,用for循环来迭代从1到5的数字,并将每个数字打印出来
# for循环 for i in range( 1 , 6 ): print(i)
1 2 3 4 5
首先, range(1, 6)
函数生成一个序列,从1到5(不包括6)。
然后,for循环使用变量 i
来迭代这个序列中的每个元素。
在每次迭代中, print(i)
语句将当前的 i
值打印出来。
再举例说明while循环:用while循环迭代从1到10的数字,并将每个数字打印出来
# while循环 i = 1 while i <= 10 : print(i) i += 1 if i == 6 : break
1 2 3 4 5
首先,将 i
初始化为1。
然后,while循环将在
i
小于或等于10时执行。在每次循环中, print(i)
语句将当前的 i
值打印出来。
接下来, i += 1
语句将 i
的值递增。在每次循环中, if i == 6: break
语句将检查 i
的值是否等于6。如果是,则使用 break
语句终止循环。
函数 Python提供了许多常用函数,这部分内容数据分析最基础的内容,有部分函数在Python内置库就已经自带
常用函数:
abs(x) # 返回x的绝对值。 round(x) # 返回最接近x的整数。如果有两个整数与x距离相等,将返回偶数。 pow(x, y) # 返回x的y次方。 sqrt(x) # 返回x的平方根。 max(x1, x2, ...) # 返回一组数中的最大值。 min(x1, x2, ...) # 返回一组数中的最小值。 sum([x1,x2,...]) # 返回可迭代对象中所有元素的和。
也有一些是来自于math库,我们需要用 from math import *
来引入math库,然后才能调用里面的函数。这个过程就好比你要使用一个工具箱里面的工具,必须先找到工具箱。通过使用这些数学函数,可以进行各种数学计算和数据处理操作。
常用数学函数
# 数学运算函数 from math import * sqrt(x) #x的算术平方根 log(x) #自然对数 log2(x) #以2为底的常用对数 log10(x) #以10为底的常用对数 exp(x) #x的e次幂 modf(x) #返回x的⼩数部分和整数部分 floor(x) #对x向下取值整 ceil(x) #对x向上取整 divmod(x,y) #接受两个数字,返回商和余数的元组(x//y , x%y)# 三角函数 sin(x) #x的正弦值 cos(x) #x的余弦值 tan(x) #x的正切值 asin(x) #x的反正弦值
acos(x) #x的反余弦值 atan(x) #x的反正切值# 排列组合函数
# from itertools import * product() #序列中的元素进行排列, 相当于使用嵌套循环组合 permutations(p[, r]) #从序列p中取出r个元素的组成全排列 combinations(p, r) #从序列p中取出r个元素组成全组合,元素不允许重复 combinations_with_replacement(p, r) #从序列p中取出r个元素组成全组合,允许重复# 简单统计函数 pandas describe() #描述性统计 count() #非空观测数量 sum() #所有值之和 mean() #平均值 median() #中位数 mode() #值的模值 std() #标准差 var() #方差 min() #所有值中的最小值 max() #所有值中的最大值 abs() #绝对值 prod() #数组元素的乘积 corr() #相关系数矩阵 cov() #协方差矩阵# 排序函数 sort() #没有返回值,会改变原有的列表 sorted() #需要用一个变量进行接收,不会修改原有列表# 集合运算符号和函数 & #交集 | #并集 - #差集 ^ #异或集(不相交的部分) intersection() #交集 union() #并集 difference() #补集 symmetric_difference() #异或集(不相交的部分) isdisjoint() #两集合有无相同元素 issubset() #是不是子集 issuperset() #是不是超集# 缺失值处理(Pandas) np.nan (Not a Number) #空值 None #缺失值 pd.NaT #时间格式的空值# 判断缺失值 isnull()/isna() #断Series或DataFrame中是否包含空值 notnull() #与isnull()结果互为取反
isin() #判断Series或DataFrame中是否包含某些值 dropna() #删除Series或DataFrame中的空值 fillna() #填充Series或DataFrame中的空值 ffill()/pad() #用缺失值的前一个值填充 bfill()/backfill() #用缺失值的后一个值填充 replace() #替换Series或DataFrame中的指定值
自定义函数 自定义函数是一种在Python中自行定义的可重复使用代码块的方法。通过定义自己的函数,可以将一系列操作放在一个函数中,并在需要时多次调用该函数。
举例说明,如何创建和调用一个自定义函数:
def linear (x) : y = 2 *x + 4 return y
在上面的例子中,我们用 def linear(x):
定义了一个名为 linear
的函数,该函数接收一个参数x。
然后函数体内计算了一个y值,它是x的两倍加上4。
这样,我们调用linea函数的时候,并传入一个参数x时,函数将返回计算得到的y值。例如,如果我们调用 linear(3)
,函数将返回 10
,因为 2*3 + 4 = 10
。
可以根据具体的需求来编写自定义函数,并在程序中调用它们。
再看一个稍微复杂一点的例子(PS:建议初学者先跳过这个例子)
# 斐波那契数列(通过迭代方法实现) def fib (n) : n1= 1 ;n2= 1 ;n3= 1 if n< 1 : print( '输入有误!' ) return 0 else : while (n -2 ) > 0 : n3 = n2 + n1 n1 = n2 n2 = n3 n -= 1 return n3 result = fib( 35 ) result
9227465
# 斐波那契数列(通过递归方法实现) def fib (n) : if n 1 : print( '输入有误!' ) return -1 elif n == 1 or n == 2 : return 1 else : return fib(n -1 ) + fib(n -2 ) result = fib( 35 ) result
9227465
# 斐波那契数列(通过矩阵方法实现) import numpy as np def dotx (a,n) : for i in range( 1 ,n+ 1 ): if i == 1 : b = a else : b = np.dot(a,b) return b def fib (n) : a = np.array([[ 1 , 1 ],[ 1 , 0 ]]) r = dotx(a,n) return r[ 0 , 1 ] result = fib( 35 ) result
第2节 1 Pandas简介 说好开始学Python,怎么到了Pandas?
前面说过,既然定义为极简入门,我们只抓核心中的核心。
那怎么样挑核心重点呢?
在你不熟悉的情况下,肯定需要请教别人,需要注意的是,不要去问应该学什么编程语言,而是去问,如果只学一门编程语言,应该学什么?
这样,问题就从多分类的选择题,变成了一道优化题!有人说选择大于努力,而现实中的情况是,我们选的不是答案只有对与错的问题,而是在好、次好与更好之间选更好,这个道理看似简单,但却不容易做到,小到你学Python应该优先学什么,大到一个国家的资源配置应该优先发展什么?本质上都是最优化问题。
回到今天的主题,如果学Python语言,但只学一个库,你要学什么?有人说人工智能好啊,我要学算法,错!算法再牛也需要你从底层的数据开始,所以答案肯定是Pandas,这属于做数据分析处理数据必知必会的内容。
今天的故事,要从08年北京奥运会那年说起,远在纽约一家量化投资公司的打工人Wes McKinney,由于在日常数据分析工作中 想多摸会儿鱼 备受Excel与SQL等工具的折磨,于是他开始构建了一个新项目 Pandas ,用来解决数据处理过程中遇到的全部任务。
Wes McKinney 什么是Pandas? Pandas是一个开源的Python库,主要用于数据分析、数据处理、数据可视化。
Pandas作为Python数据分析的核心包,提供了大量的数据分析函数,包括数据处理、数据抽取、数据集成、数据计算等基本的数据分析手段。
Pandas的名称来自于面板数据(panel data)和python数据分析(data analysis)的简称。
千万记得Pandas不是咱们的国宝大熊猫的复数形式!!!(尽管这一强调极有可能适得其反,但还是忍不住贴一张panda的图)
Pandas 为什么Pandas必学? 支持数值、文本和可视化,快速灵活完成数据需求!强大
如果用一个字来说明,那就是“快”。这个快指的是你从开始构思到写完代码的时间,毫不夸张地说,当你把数据需求用文字梳理清楚的时候,基本上也就相当于用Python实现了这一过程,因为在Python的世界,所见即所得
怎么学Pandas? 把大象放进冰箱里需要三步,打开冰箱门、把大象塞进去、关上冰箱门。同样地,怎么学Pandas,也需要三步
第一步,必须了解Pandas的数据结构。在之前的系列文章里面说过,学习语言学习的三板斧,数据结构,流程控制,自定义函数。这里pandas虽然只是一个库,但同样有其数据结构。
第二步,必须学会用Pandas做数据处理。这是你做数据分析的基本功,里面包含如下内容
第三步,掌握一些Pandas高阶与展示技巧。这是你分析或展示的必备技能
第2节 2 Pandas数据类型
Pandas 有两种自己独有的基本数据结构。需要注意的是,它固然有着两种数据结构,因为它依然是 Python 的一个库,所以 Python 中有的数据类型在这里依然适用。我们分别看一下这两种数据结构:
Series Series:一维数组。该结构能够放置各种数据类型,比如字符、整数、浮点数等
我们先引入pandas包,这里有一个约定成俗的写法 import pandas as pd
将pandas引入,并命其别名为pd
接着将列表 [2,3,5,7,11]
放到pd.Series()里面
import pandas as pd s = pd.Series([ 2 , 3 , 5 , 7 , 11 ],name = 'A' ) s
0 2 1 3 2 5 3 7 4 11 Name: A, dtype: int64
Time- Series:以时间为索引的Series
同样的,将列 ['2024-01-01 00:00:00', '2024-01-01 03:00:00','2024-01-01 06:00:00']
放到pd.DatetimeIndex()里面
dts1 = pd.DatetimeIndex([ '2024-01-01 00:00:00' , '2024-01-01 03:00:00' , '2024-01-01 06:00:00' ]) dts1
DatetimeIndex(['2024-01-01 00:00:00', '2024-01-01 03:00:00', '2024-01-01 06:00:00'], dtype='datetime64[ns]', freq=None)
还有另外一种写法 pd.date_range
可以按一定的频率生成时间序列
dts2 = pd.date_range(start= '2024-01-01' , periods= 6 , freq= '3H' ) dts2
DatetimeIndex(['2024-01-01 00:00:00', '2024-01-01 03:00:00', '2024-01-01 06:00:00', '2024-01-01 09:00:00', '2024-01-01 12:00:00', '2024-01-01 15:00:00'], dtype='datetime64[ns]', freq='3H')
dts3 = pd.date_range( '2024-01-01' , periods= 6 , freq= 'd' ) dts3
DatetimeIndex(['2024-01-01', '2024-01-02', '2024-01-03', '2024-01-04', '2024-01-05', '2024-01-06'], dtype='datetime64[ns]', freq='D')
DataFrame DataFrame:二维的表格型数据结构,可以理解为Series的容器,通俗地说,就是可以把Series放到DataFrame里面。
它是一种二维表格型数据的结构,既有行索引,也有列索引。行索引是 index,列索引是 columns。类似于初中数学里,在二维平面里用坐标轴来定位平面中的点。
注意,DataFrame又是Pandas的核心!接下来的内容基本上以DataFrame为主
先来看看如何创建DataFrame,上面说过Series也好,DataFrame也罢,本质上都是容器。
千万别被”容器“这个词吓住了,通俗来说,就是里面可以放东西的东西。
从字典创建DataFrame
相当于给里面放dict:先创建一个字典 d
,再把 d
放进了 DataFrame
里命名为 df
d = { 'A' : [ 1 , 2 , 3 ], 'B' : [ 4 , 5 , 6 ], 'C' : [ 7 , 8 , 9 ]} df = pd.DataFrame(data = d) df
从列表创建DataFrame
先创建了一个列表 d
,再把 d
放进了 DataFrame
里命名为 df
d = [[ 4 , 7 , 10 ],[ 5 , 8 ,
11 ],[ 6 , 9 , 12 ]] df1 = pd.DataFrame( data = d, index=[ 'a' , 'b' , 'c' ], columns=[ 'A' , 'B' , 'C' ]) df1
从数组创建DataFrame
数组(array)对你来说可能是一个新概念,在Python里面,创建数组需要引入一个类似于Pandas的库,叫做Numpy。与前面引入Pandas类似,我们用 import numpy as np
来引入numpy,命其别名为np。
同样的,先创建一个数组 d
,再把 d
放进了 DataFrame
里命名为 df
import numpy as np d = np.array([[ 1 , 2 , 3 ], [ 4 , 5 , 6 ], [ 7 , 8 , 9 ]]) df2 = pd.DataFrame(data = d, index=[ 'a' , 'b' , 'c' ], columns=[ 'A' , 'B' , 'C' ]) df2
以上,我们用了不同的方式来创建DataFrame,接下来,我们看看创建好后,如何查看数据
第2节 3 Pandas数据查看 这里我们创建一个 DataFrame
命名为 df
:
import numpy as np import pandas as pd d = np.array([[ 81 , 28 , 24 , 25 , 96 ], [ 8 , 35 , 56 , 98 , 39 ], [ 13 , 39 , 55 , 36 , 3 ], [ 70 , 54 , 69 , 48 , 12 ], [ 63 , 80 , 97 , 25 , 70 ]]) df = pd.DataFrame(data = d, columns=list( 'abcde' )) df
查看前n行
df.head( 2 )
查看后n行
df.tail( 2 )
查看随机N行
df.sample( 2 )
按列选取 单列选取,我们有3种方式可以实现
第一种,直接在 []
里面写上要筛选的列名
df[ 'a' ]
0 81 1 8 2 13 3 70 4 63 Name: a, dtype: int64
第二种,在 .iloc[]
里的 ,
前面写上要筛选的行索引,在 ,
后面写上要删选的列索引。其中 :
代表所有, 0:3
代表从索引0到2
df.iloc[ 0 : 3 , 0 ]
0 81 1 8 2 13 Name: a, dtype: int64
第三种,直接 .
后面写上列名
df.a
0 81 1 8 2 13 3 70 4 63 Name: a, dtype: int64
同样的,选择多列常见的也有3种方式:
第一种,直接在 []
里面写上要筛选的列名组成的列表 ['a','c','d']
df[[ 'a' , 'c' , 'd' ]]
第二种,在 .iloc[]
里面行索引位置写 :
选取所有行,列索引位置写上要筛选的列索引组成的列表 [0,2,3]
df.iloc[:,[ 0 , 2 , 3 ]]
第三种,在 .loc[]
里面的行索引位置写
:
来选取所有行,在列索引位置写上要筛选的列索引组成的列表 ['a','c','d']
df.loc[:,[ 'a' , 'c' , 'd' ]]
按行选取 直接选取第一行
df[ 0 : 1 ]
用 loc
选取第一行
df.loc[ 0 : 0 ]
选取任意多行
df.iloc[[ 1 , 3 ],]
选取连续多行
df.iloc[ 1 : 4 ,:]
指定行列 指定行列值
df.iat[ 2 , 2 ] # 根据行列索引
55
df.at[ 2 , 'c' ] # 根据行列名称
55
指定行列区域
df.iloc[[ 2 , 3 ],[ 1 , 4 ]]
以上是关于如何查看一个DataFrame里的数据,包括用 []
、 iloc
、
iat
等方式选取数据,接下来我们来看如何用条件表达式来筛选数据:
第2节 4 Pandas条件查询 在pandas中,可以使用条件筛选来选择满足特定条件的数据
import pandas as pd d = np.array([[ 81 , 28 , 24 , 25 , 96 ], [ 8 , 35 , 56 , 98 , 39 ], [ 13 , 39 , 55 , 36 , 3 ], [ 70 , 54 , 69 , 48 , 12 ], [ 63 , 80 , 97 , 25 , 70 ]]) df = pd.DataFrame(data = d, columns=list( 'abcde' )) df
# 单一条件 df[df[ 'a' ]> 60 ] df.loc[df[ 'a' ]> 60 ]
# 单一条件&多列 df.loc[(df[ 'a' ]> 60 ) ,[ 'a' , 'b' , 'd' ]]
# 多条件 df[(df[ 'a' ]> 60 ) & (df[ 'b' ]> 60 )]
# 多条件 筛选行 & 指定列筛选列 df.loc[(df[ 'a' ]> 60 ) & (df[ 'b' ]> 60 ) ,[ 'a' , 'b' , 'd' ]]
以上是使用条件筛选来选取数据 ,接下来我们来看如何对数据进行数学计算
第2节 5 Pandas数学计算 import pandas as pd d = np.array([[ 81 , 28 , 24 , 25 , 96 ], [ 8 , 35 , 56 , 98 , 39 ], [ 13 , 39 , 55 , 36 , 3 ], [ 70 , 54 , 69 , 48 , 12 ], [ 63 , 80 , 97 , 25 , 70 ]]) df = pd.DataFrame(data = d, columns=list( 'abcde' )) df
聚合计算 聚合计算是指对数据进行汇总和统计的操作。常用的聚合计算方法包括计算均值、求和、最大值、最小值、计数等。
df[ 'a' ].mean()
47.0
df[ 'a' ].sum()
235
df[ 'a' ].max()
81
df[ 'a' ].min()
8
df[ 'a' ].count()
5
df[ 'a' ].median() # 中位数
63.0
df[ 'a' ].var() #方差
1154.5
df[ 'a' ].skew() # 偏度
-0.45733193928530436
df[ 'a' ].kurt() # 峰度
-2.9999915595685325
df[ 'a' ].cumsum() # 累计求和
0 81 1 89 2 102 3 172 4 235 Name: a, dtype: int64
df[ 'a' ].cumprod() # 累计求积
0 81 1 648 2 8424 3 589680 4 37149840 Name: a, dtype: int64
df[ 'a' ].diff() # 差分
0 NaN 1 -73.0 2 5.0 3 57.0 4 -7.0 Name: a, dtype: float64
df[ 'a' ].mad() # 平均绝对偏差
29.2
按行、列聚合计算 df.sum(axis= 0 ) # 按列求和汇总到最后一行
a 235 b 236 c 301 d 232 e 220 dtype: int64
df.sum(axis= 1 ) # 按行求和汇总到最后一列
0 254 1 236 2 146 3 253 4 335 dtype: int64
df.describe() # 描述性统计
agg函数 对整个DataFrame批量使用多个聚合函数
df.agg([ 'sum' , 'mean' , 'max' , 'min' , 'median' ])
对DataFramed的某些列应用不同的聚合函数
df.agg({ 'a' :[ 'max' , 'min' ], 'b' :[ 'sum' , 'mean' ], 'c' :[ 'median' ]})
apply、applymap、map函数 在Python中如果想要对数据使用函数,可以借助apply(),applymap(),map()对数据进行转换,括号里面可以是直接函数式,或者自定义函数(def)或者匿名函数(lambad)
1、当我们要对数据框(DataFrame)的数据进行按行或按列操作时用apply()
df.apply( lambda x :x.max()-x.min(),axis= 1 ) #axis=1,表示按行对数据进行操作 #从下面的结果可以看出,我们使用了apply函数之后,系统自动按行找最大值和最小值计算,每一行输出一个值
0 72 1 90 2 52 3 58 4 72 dtype: int64
df.apply( lambda x :x.max()-x.min(),axis= 0 ) #默认参数axis=0,表示按列对数据进行操作 #从下面的结果可以看出,我们使用了apply函数之后,系统自动按列找最大值和最小值计算,每一列输出一个值
a 73 b 52 c 73 d 73 e 93 dtype: int64
2、当我们要对数据框(DataFrame)的每一个数据进行操作时用applymap(),返回结果是DataFrame格式
df.applymap( lambda x : 1 if x> 60 else 0 ) #从下面的结果可以看出,我们使用了applymap函数之后, #系统自动对每一个数据进行判断,判断之后输出结果
3、当我们要对Series的每一个数据进行操作时用map()
df[ 'a' ].map( lambda x : 1 if x> 60 else 0 )
0 1 1 0 2 0 3 1 4 1 Name: a, dtype: int64
总结:
apply()
函数可以在DataFrame或Series上应用自定义函数,可以在行或列上进行操作。
applymap()
函数只适用于DataFrame,可以在每个元素上应用自定义函数。
map()
函数只适用于Series,用于将每个元素映射到另一个值。
以上是数学运算部分,包括聚合计算、批量应用聚合函数,以及对Series和DataFrame进行批量映射,接下来我们来看如何对数据进行合并拼接
第2节 6 Pandas合并连接
在pandas中,有多种方法可以合并和拼接数据。常见的方法包括 append()
、 concat()
、 merge()
。
追加(Append) append()函数用于将一个DataFrame或Series对象追加到另一个DataFrame中。
import pandas as pd df1 = pd.DataFrame({ 'A' : [ 'a' , 'b' ], 'B' : [ 1 , 2 ]}) df1
df2 = pd.DataFrame({ 'A' : [ 'b' , 'c' , 'd' ], 'B' : [ 2 , 3 , 4 ]}) df2
df1.append(df2,ignore_index= True )
合并(Concat) concat()函数用于沿指定轴将多个对象(比如Series、DataFrame)堆叠在一起。可以沿行或列方向进行拼接。 先看一个上下堆叠的例子
df1 = pd.DataFrame({ 'A' : [ 'a' , 'b' ], 'B' : [ 1 , 2 ]}) df1
df2 = pd.DataFrame({ 'A' : [
'b' , 'c' , 'd' ], 'B' : [ 2 , 3 , 4 ]}) df2
pd.concat([df1,df2],axis = 0 ) # 上下拼接
再看一个左右堆叠的例子
df1 = pd.DataFrame({ 'A' : [
'a' , 'b' ]}) df1
df2 = pd.DataFrame({ 'B' : [ 1 , 2 ], 'C' : [ 2 , 4 ]}) df2
pd.concat([df1,df2],axis = 1 ) # 左右拼接
连接(Merge) merge()
函数用于根据一个或多个键将两个DataFrame的行连接起来。类似于SQL中的JOIN操作。
数据连接 1 (pd.merge) 先看一下 inner
和 outer
连接
df1 = pd.DataFrame({ 'A' : [ 'a' , 'b' , 'c' ], 'B' : [ 1 , 2 , 3 ]}) df1
df2 = pd.DataFrame({ 'A' : [ 'b' , 'c' , 'd' ], 'B' : [ 2 , 3 , 4 ]}) df2
pd.merge(df1,df2,how = 'inner' )
pd.merge(df1,df2,how = 'outer' )
数据连接 2 (pd.merge) 再看左右链接的例子:
df1 = pd.DataFrame({ 'A' : [ 'a' , 'b' , 'c' ], 'B' : [ 1 , 2 , 3 ]}) df1
df2 = pd.DataFrame({ 'A' : [ 'b' , 'c' , 'd' ], 'C' : [ 2 , 3 , 4 ]}) df2
pd.merge(df1,df2,how = 'left' ,on = "A" ) # 左连接
pd.merge(df1,df2,how = 'right' ,on = "A" ) # 右连接
pd.merge(df1,df2,how = 'inner' ,on = "A" ) # 内连接
pd.merge(df1,df2,how = 'outer' ,on = "A" ) # 外连接
补充1个小技巧
df1[df1[ 'A' ].isin(df2[ 'A' ])] # 返回在df1中列'A'的值在df2中也存在的行
df1[~df1[ 'A' ].isin(df2[ 'A' ])] # 返回在df1中列'A'的值在df2中不存在的行
第2节 7 Pandas分组聚合 分组聚合(group by)顾名思义就是分2步:
先分组 :根据某列数据的值进行分组。用 groupby()
对某列进行分组 后聚合 :将结果应用聚合函数进行计算。在 agg()
函数里应用聚合函数计算结果,如 sum()、mean()、count()、max()、min()
等,用于对每个分组进行聚合计算。 import pandas as pd import numpy as np import random
df = pd.DataFrame({ 'A' : [ 'a' , 'b' , 'a' , 'b' , 'a' , 'b' ], 'B' : [ 'L' , 'L' , 'M' , 'N' , 'M' , 'M' ], 'C' : [ 107 , 177 , 139 , 3 , 52 , 38 ], 'D' : [ 22 , 59 , 38 , 50 , 60 , 82 ]}) df
单列分组
① 对单列分组后应用 sum
聚合函数
df.groupby( 'A' ).sum()
② 对单列分组后应用单个指定的聚合函数
df.groupby( 'A' ).agg({ 'C' : 'min' }).rename(columns={ 'C' : 'C_min' })
③ 对单列分组后应用多个指定的聚合函数
df.groupby([ 'A' ]).agg({ 'C' : 'max' , 'D' : 'min' }).rename(columns={ 'C' : 'C_max' , 'D' : 'D_min' })
两列分组
① 对多列分组后应用 sum
聚合函数:
df.groupby([ 'A' , 'B' ]).sum()
② 对两列进行 group
后,都应用 max
聚合函数
df.groupby([ 'A' , 'B' ]).agg({ 'C' : 'max' }).rename(columns={ 'C' : 'C_max' })
③ 对两列进行分组 group
后,分别应用 max
、 min
聚合函数
df.groupby([ 'A' , 'B' ]).agg({ 'C' : 'max' , 'D' : 'min' }).rename(columns={ 'C' : 'C_max' , 'D' : 'D_min' })
补充1: 应用自定义的聚合函数
df = pd.DataFrame({ 'A' : [ 'a' , 'b' , 'a' , 'b' , 'a' , 'b' ], 'B' : [ 'L' , 'L' , 'M' , 'N' , 'M' , 'M' ], 'C' : [ 107 , 177 , 139 , 3 , 52 , 38 ], 'D' : [ 22 , 59 , 38 , 50 , 60 , 82 ]}) df
# 使用自定义的聚合函数计算每个分组的最大值和最小值 def custom_agg (x) : return x.max() - x.min() result = df[[ 'B' , 'C' ]].groupby( 'B' ).agg({ 'C' : custom_agg}) result
补充2: 开窗函数(类似于SQL里面的 over partition by
):
使用transform函数计算每个分组的均值
# 使用transform函数计算每个分组的均值 df[ 'B_C_std' ] = df[[ 'B' , 'C' ]].groupby( 'B' )[ 'C' ].transform( 'mean' )
df
补充3: 分组聚合拼接字符串 pandas实现类似 group_concat 功能
假设有这样一个数据:
df = pd.DataFrame({ '姓名' : [ '张三' , '张三' , '张三' , '李四' , '李四' , '李四' ], '科目' : [ '语文' , '数学'
, '英语' , '语文' , '数学' , '英语' ] }) df
补充:按某列分组,将另一列文本拼接合并
按名称分组,把每个人的科目拼接到一个字符串:
# 对整个group对象中的所有列应用join 连接元素 (df.astype(str) # 先将数据全转为字符 .groupby( '姓名' ) # 分组 .agg( lambda x : ',' .join(x)))[[ '科目' ]] # join 连接元素
第2节 8-1 Pandas 数据重塑 - 数据变形 数据重塑(Reshaping) 数据重塑,顾名思义就是给数据做各种变形,主要有以下几种:
df.pivot( ) 数据变形 根据索引(index)、列(column)(values)值), 对原有DataFrame(数据框)进行变形重塑,俗称长表转宽表
import pandas as pd import numpy as np
df = pd.DataFrame( { '姓名' : [ '张三' , '张三' , '张三' , '李四' , '李四' , '李四' ], '科目' : [ '语文' , '数学' , '英语' , '语文' , '数学' , '英语' ], '成绩' : [ 91 , 80 , 100 , 80 , 100 , 96 ]}) df
长转宽:使用 df.pivot
以 姓名
为 index
,以各 科目
为 columns
,来统计各科成绩:
df = pd.DataFrame( { '姓名' : [ '张三' , '张三' , '张三' , '李四' , '李四' , '李四' ], '科目' : [ '语文' , '数学' , '英语' , '语文' , '数学' , '英语' ], '成绩' : [ 91 , 80 , 100 , 80 , 100 , 96 ]}) df
df.pivot(index= '姓名' , columns= '科目' , values= '成绩' )
pd.melt() 数据融合 df = pd.DataFrame( { '姓名' : [ '张三' , '张三' , '张三' , '李四' , '李四' , '李四' ], '科目' : [ '语文' , '数学' , '英语' , '语文' , '数学' , '英语' ], '成绩' : [ 91 , 80 , 100 , 80 , 100 , 96 ]}) df1 = pd.pivot(df, index= '姓名' , columns= '科目' , values= '成绩' ).reset_index() df1
宽表变长表:使用 df.pivot
以 姓名
为标识变量的列 id_vars
,以各 科目
为 value_vars
,来统计各科成绩:
df1.melt(id_vars=[ '姓名' ], value_vars=[ '数学' , '英语' , '语文' ])
pd.pivot_table() 数据透视 random.seed( 1024 ) df = pd.DataFrame( { '专业' : np.repeat([ '数学与应用数学' , '计算机' , '统计学' ], 4 ), '班级' : [ '1班' , '1班' , '2班' , '2班' ]* 3 , '科目' : [ '高数' , '线代' ] * 6 , '平均分' : [random.randint( 60 , 100 ) for i in range( 12 )], '及格人数' : [random.randint( 30 ,
50 ) for i in range( 12 )]}) df
各个专业对应科目的及格人数和平均分
pd.pivot_table(df, index=[ '专业' , '科目' ], values=[ '及格人数' , '平均分' ], aggfunc={ '及格人数' :np.sum, "平均分" :np.mean})
补充说明:
df.pivot_table()
和 df.pivot()
都是Pandas中用于将长表转换为宽表的方法,但它们在使用方式和功能上有一些区别。
df.pivot()
方法接受三个参数: index
、 columns
和 values
,分别指定新表的索引、列和值。 df.pivot_table()
方法接受多个参数,其中最重要的是 index
、 columns
和 values
,用于指定新表的索引、列和值。此外,还可以使用 aggfunc
参数指定对重复值进行聚合操作的函数,默认为均值。 df.pivot()
方法在长表中存在重复值时会引发错误。因此,如果长表中存在重复值,就需要先进行去重操作,或者使用其他方法来处理重复值。 df.pivot_table()
方法可以在长表中存在重复值的情况下进行透视操作,并可以使用 aggfunc
参数指定对重复值进行聚合操作的函数,默认为均值。
df.pivot()
方法不支持对重复值进行聚合操作,它只是简单地将长表中的数据转换为宽表。 df.pivot_table()
方法支持对重复值进行聚合操作。可以使用 aggfunc
参数来指定聚合函数,例如求均值、求和、计数等。 总的来说, df.pivot()
方法适用于长表中不存在重复值的情况,而 df.pivot_table()
方法适用于长表中存在重复值的情况,并且可以对重复值进行聚合操作。根据具体的数据结构和分析需求,选择合适的方法来进行转换操作。
第2节 8-2 Pandas 数据重塑 - 数据堆叠 数据堆叠 df = pd.DataFrame({ '专业' : np.repeat([ '数学与应用数学' , '计算机' , '统计学' , '物理学' ], 6 ), '班级' : [ '1班' , '2班' , '3班' ]* 8 , '科目' : [ '高数' , '线代' ] * 12 , '平均分' : [random.randint( 60 , 100 ) for i in range( 24 )], '及格人数' : [random.randint( 30 , 50 ) for i in range( 24 )]}) df2 = pd.pivot_table(df, index=[ '专业' , '科目' ], values=[ '及格人数' , '平均分' ], aggfunc={ '及格人数' :np.sum, "平均分" :np.mean}) df2
stacked = df2.stack()
“压缩”后的DataFrame或Series(具有MultiIndex作为索引), stack() 的逆操作是unstack(),默认情况下取消最后压缩的那个级别:
堆叠 stack()
,顾名思义就是把透视结果堆到一起。接下来我们把透视后堆叠的数据一步步展开 unstack()
:
stacked.unstack()
stacked.unstack(level= 1 )
stacked.unstack(level= 0 )
第2节 8-3 Pandas 数据重塑 - 数据交叉表 数据交叉表 交叉表显示了每个变量的不同类别组合中观察到的频率或计数。通俗地说,就是根据不同列的数据统计了频数
df = pd.DataFrame( { 'High' : [ "高" , "高" , "高" , "中" , "中" , "中" , "低" , "低" , "低" , "高" , "低" ], 'Weight' : [ "重" , "轻" , "中" , "中" , "轻" , "重" , "重" , "轻" , "中" , "重" , "轻" ] }) df
pd.crosstab(df[ 'High' ], df[ 'Weight' ])
双层 crosstab
df = pd.DataFrame( { 'High' : [ "高" , "高" , "高" , "中" , "中" , "中" , "低" , "低" , "低" , "高" , "低" ], 'Weight' : [ "重" , "轻" , "中" , "中" , "轻" , "重" , "重" , "轻" , "中" , "重" , "轻" ], 'Size' : [ "大" , "中" , "小" , "中" , "中" , "大" ,
"中" , "小" , "小" , "大" , "小" ]}) df
pd.crosstab(df[ 'High' ], [df[ 'Weight' ], df[ 'Size' ]], rownames=[ 'High' ], colnames=[ 'Weight' , 'Size' ])
另一种 宽表转长表 pd.wide_to_long()
np.random.seed( 123 ) df = pd.DataFrame({ "A1970" : { 0 : "a" , 1 : "b" , 2 : "c" }, "A1980" : { 0 : "d" , 1 : "e" , 2 : "f" }, "B1970" : { 0 : 2.5 , 1 : 1.2 , 2 : .7 }, "B1980" : { 0 : 3.2 , 1 : 1.3 , 2 : .1 }, "X" : dict(zip(range( 3 ), np.random.randn( 3 ))) }) df[ "id" ] = df.index df
把 id
列用作标识列
pd.wide_to_long(df, [ "A" , "B" ], i= "id" , j= "year" )
df = pd.DataFrame({ 'famid' : [ 1 , 1 , 1 , 2 , 2 , 2 , 3 , 3 , 3 ], 'birth' : [ 1 , 2 , 3 , 1 , 2 , 3 , 1 , 2 , 3 ], 'ht1' : [ 2.8 , 2.9 , 2.2 , 2 , 1.8 , 1.9 , 2.2 , 2.3 , 2.1 ], 'ht2' : [ 3.4 , 3.8 , 2.9 , 3.2 , 2.8 , 2.4 , 3.3 , 3.4 , 2.9 ] }) df
把 famid
, birth
两列用作标识列
l = pd.wide_to_long(df, stubnames= 'ht' , i=[ 'famid' , 'birth' ], j= 'age' ) l
第3节 9 Pandas 文本数据 import pandas as pd
1、cat() 拼接字符串 d = pd.DataFrame([ 'a' , 'b' , 'c' ],columns = [ 'A' ]) d
将某列元素拼接一列特定字符串
d[ 'A' ].str.cat([ 'A' , 'B' , 'C' ], sep= ',' )
0 a,A 1 b,B 2 c,C Name: A, dtype: object
将某列的元素合并为一个字符串
d[ 'A' ].str.cat(sep= ',' )
'a,b,c'
2、split() 切分字符串 import numpy as np import pandas as pd d = pd.DataFrame([ 'a_b_c' , 'c_d_e' , np.nan, 'f_g_h' ],columns = [ 'A' ]) d
将某列的字符串元素进行切分
d[ 'A' ].str.split( '_' )
0 [a, b, c] 1 [c, d, e] 2 NaN 3 [f, g, h] Name: A, dtype: object
3、get() 获取指定位置的字符串 d = pd.DataFrame([ 'a_b_c' , 'c_d_e' , np.nan, 'f_g_h' ],columns = [ 'A' ]) d[ 'A' ]
0 a_b_c 1 c_d_e 2 NaN 3 f_g_h Name: A, dtype: object
d[ 'A' ].str.get( 2 )
0 b 1 d 2 NaN 3 g Name: A, dtype: object
4、join() 对每个字符都用给定的字符串拼接起来(不常用) d = pd.DataFrame([
'a_b_c' , 'c_d_e' , np.nan, 'f_g_h' ],columns = [ 'A' ]) d[ 'A' ]
0 a_b_c 1 c_d_e 2 NaN 3 f_g_h Name: A, dtype: object
d[ 'A' ].str.join( "!" )
0 a!_!b!_!c 1 c!_!d!_!e 2 NaN 3 f!_!g!_!h Name: A, dtype: object
5、contains() 是否包含表达式 (很常用) d[ 'A' ].str.contains( 'd' )
0 False 1 True 2 NaN 3 False Name: A, dtype: object
d.fillna( '0' )[d.fillna( '0' )[ 'A' ].str.contains( 'd' )]
d.fillna( '0' )[d[ 'A' ].fillna( '0' ).str.contains( 'd|e' )] #表示或的关系用"A|B",表示且用'A.*B|B.*A'
6、replace() 替换 d[ 'A' ].str.replace( "_" , "." )
0 a.b.c 1 c.d.e 2 NaN 3 f.g.h Name: A, dtype: object
7、repeat() 重复 d[ 'A' ].str.repeat( 3 )
0 a_b_ca_b_ca_b_c 1 c_d_ec_d_ec_d_e 2 NaN 3 f_g_hf_g_hf_g_h Name: A, dtype: object
8、pad() 左右补齐 d[ 'A' ].str.pad( 10 , fillchar= "0" )
0 00000a_b_c 1 00000c_d_e 2 NaN 3 00000f_g_h Name: A, dtype: object
d[ 'A' ].str.pad( 10 , side= "right" , fillchar= "?" )
0 a_b_c????? 1 c_d_e????? 2 NaN 3 f_g_h????? Name: A, dtype: object
9、center() 中间补齐 d[ 'A' ].str.center( 10 , fillchar= "?" )
0 ??a_b_c??? 1 ??c_d_e??? 2 NaN 3 ??f_g_h??? Name: A, dtype: object
10、ljust() 右边补齐 d[ 'A' ].str.ljust( 10 , fillchar= "?" )
0 a_b_c????? 1 c_d_e????? 2 NaN 3 f_g_h????? Name: A, dtype: object
11、rjust() 左边补齐 d[ 'A' ].str.rjust( 10 , fillchar= "?" )
0 ?????a_b_c 1 ?????c_d_e 2 NaN 3 ?????f_g_h Name: A, dtype: object
12、zfill() 左边补0 d[ 'A' ].str.zfill( 10 )
0 00000a_b_c 1 00000c_d_e 2 NaN 3 00000f_g_h Name: A, dtype: object
13、wrap() 在指定的位置加回车符号 d[ 'A' ].str.wrap( 3 )
0 a_b\n_c 1 c_d\n_e 2 NaN 3 f_g\n_h Name: A, dtype: object
14、slice() 按给定点的开始结束位置切割字符串 d[ 'A' ].str.slice( 1 , 3 )
0 _b 1 _d 2 NaN 3 _g Name: A, dtype: object
15、slice_replace() 使用给定的字符串,替换指定的位置的字符 d[ 'A' ].str.slice_replace( 1 , 3 , "?" )
0 a?_c 1 c?_e 2 NaN 3 f?_h Name: A, dtype: object
16、count() 计算给定单词出现的次数 d[ 'A' ].str.count( "b" )
0 1.0 1 0.0 2 NaN 3 0.0 Name: A, dtype: float64
17、startswith() 判断是否以给定的字符串开头 d[ 'A' ].str.startswith( "a" )
0 True 1 False 2 NaN 3 False Name: A, dtype: object
18、endswith() 判断是否以给定的字符串结束 d[ 'A' ].str.endswith( "e" )
0 False 1 True 2 NaN 3 False Name: A, dtype: object
19、findall() 查找所有符合正则表达式的字符,以数组形式返回 d[ 'A' ].str.findall( "[a-z]" )
0 [a, b, c] 1 [c, d, e] 2 NaN 3 [f, g, h] Name: A, dtype: object
20、match() 检测是否全部匹配给点的字符串或者表达式 d[ 'A' ].str.match( "[d-z]" )
0 False 1 False 2 NaN 3 True Name: A, dtype: object
21、extract() 抽取匹配的字符串出来,注意要加上括号,把你需要抽取的东西标注上 d[ 'A' ].str.extract( "([d-z])" )
22、len() 计算字符串的长度 d[ 'A' ].str.len()
0 5.0 1 5.0 2 NaN 3 5.0 Name: A, dtype: float64
23、strip() 去除前后的空白字符 df = pd.DataFrame([ 'a_b ' , ' d_e ' , np.nan, 'f_g ' ],columns = [ 'B' ]) df[ 'B' ]
0 a_b 1 d_e 2 NaN 3 f_g Name: B, dtype: object
df[ 'B' ].str.strip()
0 a_b 1 d_e 2 NaN 3 f_g Name: B, dtype: object
24、rstrip() 去除后面的空白字符
df[ 'B' ].str.rstrip()
0 a_b 1 d_e 2 NaN 3 f_g Name: B, dtype: object
25、lstrip() 去除前面的空白字符 df[ 'B' ].str.lstrip()
0 a_b 1 d_e 2 NaN 3 f_g Name: B, dtype: object
26、partition() 把字符串数组切割称为DataFrame,注意切割只是切割称为三部分,分隔符前,分隔符,分隔符后 d[ 'A' ] .str.partition( '_' )
27、rpartition() 从右切起 d[ 'A' ].str.rpartition( '_' )
28、lower() 全部小写 d[ 'A' ].str.lower()
0 a_b_c 1 c_d_e 2 NaN 3 f_g_h Name: A, dtype: object
29、upper() 全部大写 d[ 'A' ].str.upper()
0 A_B_C 1 C_D_E 2 NaN 3 F_G_H Name: A, dtype: object
30、find() 从左边开始,查找给定字符串的所在位置 d[ 'A' ].str.find( 'd' )
0 -1.0 1 2.0 2 NaN 3 -1.0 Name: A, dtype: float64
31、rfind() 从右边开始,查找给定字符串的所在位置 d[ 'A' ].str.rfind( 'd' )
0 -1.0 1 2.0 2 NaN 3 -1.0 Name: A, dtype: float64
32、index() 查找给定字符串的位置,注意,如果不存在这个字符串,那么会报错! d[ 'A' ].str.index( '_' )
0 1.0 1 1.0 2 NaN 3 1.0 Name: A, dtype: float64
33、rindex() 从右边开始查找,给定字符串的位置 d[ 'A' ].str.rindex( '_'
)
0 3.0 1 3.0 2 NaN 3 3.0 Name: A, dtype: float64
34、capitalize() 首字符大写 d[ 'A' ].str.capitalize()
0 A_b_c 1 C_d_e 2 NaN 3 F_g_h Name: A, dtype: object
35、swapcase() 大小写互换 d[ 'A' ].str.capitalize()
0 A_b_c 1 C_d_e 2 NaN 3 F_g_h Name: A, dtype: object
36、isalnum() 是否全部是数字和字母组成 d[ 'A' ].str.isalnum()
0 False 1 False 2 NaN 3 False Name: A, dtype: object
37、isalpha() 是否全部是字母 d[ 'A' ].str.isalpha()
0 False 1 False 2 NaN 3 False Name: A, dtype: object
38、isdigit() 是否全部都是数字 d[ 'A' ].str.isdigit()
0 False 1 False 2 NaN 3 False Name: A, dtype: object
39、isspace() 是否空格 d[ 'A' ].str.isspace()
0 False 1 False 2 NaN 3 False Name: A, dtype: object
40、islower() 是否全部小写 d[ 'A' ].str.islower()
0 True 1 True 2 NaN 3 True Name: A, dtype: object
41、isupper() 是否全部大写 d[ 'A' ].str.isupper()
0 False 1 False 2 NaN 3 False Name: A, dtype: object
42、istitle() 是否只有首字母为大写,其他字母为小写 d[ 'A' ].str.istitle()
0 False 1 False 2 NaN 3 False Name: A, dtype: object
43、isnumeric() 是否是数字 d[ 'A' ].str.isnumeric()
0 False 1 False 2 NaN 3 False Name: A, dtype: object
44、isdecimal() 是否全是数字 d[ 'A' ].str.isdecimal()
0 False 1 False 2 NaN 3 False Name: A, dtype: object
第3节 10 Pandas 时序数据 在Pandas中,时间序列(Time Series)是一种特殊的数据类型,用于处理时间相关的数据。Pandas提供了丰富的功能和方法,方便对时间序列数据进行处理和分析。下面是一些针对时间序列的常用操作:
创建时间序列数据 方式① 使用 to_datetime
创建时间序列:直接传入列表即可
import pandas as pd # 将列表转换为时间戳 date_range = pd.to_datetime([ '2024-01-01' , '2024-01-02' , '2024-01-03' ]) date_range
DatetimeIndex(['2024-01-01', '2024-01-02', '2024-01-03'], dtype='datetime64[ns]', freq=None)
方式② 使用 pd.date_range()
创建一段连续的时间范围:使用指定参数即可
import pandas as pd date_range = pd.date_range(start= '2024-01-01' , end= '2024-12-31' , freq= 'D' ) date_range
DatetimeIndex(['2024-01-01', '2024-01-02', '2024-01-03', '2024-01-04', '2024-01-05', '2024-01-06', '2024-01-07', '2024-01-08', '2024-01-09', '2024-01-10', ... '2024-12-22', '2024-12-23', '2024-12-24', '2024-12-25',
'2024-12-26', '2024-12-27', '2024-12-28', '2024-12-29', '2024-12-30', '2024-12-31'], dtype='datetime64[ns]', length=366, freq='D')
其中,start是起始日期,end是结束日期,freq是频率,这里设置为'D'表示每天。
方式③ 使用 Timestamp()
函数创建一个特定的时间戳:使用指定参数即可
import pandas as pd timestamp = pd.Timestamp(year= 2023 , month= 1 , day= 1 , hour= 12 , minute= 30 , second= 45 ) timestamp
Timestamp('2023-01-01 12:30:45')
方式④ 使用 datetime 模块创建时间戳:使用指定参数即可
import pandas as pd from datetime import datetime timestamp = datetime( 2023 , 1 , 1 , 12 , 30 , 45 ) print(timestamp)
2023-01-01 12:30:45
时长数据计算 计算一下两个时间数据之差
import pandas as pd # 创建两个固定时间 start_time = pd.Timestamp( '2024-01-01 12:00:00' ) end_time = pd.Timestamp( '2024-01-02 14:30:00' ) # 计算时间差 time_diff = end_time - start_time time_diff
Timedelta('1 days 02:30:00')
一个固定时间加上 pd.Timedelta
类型的时间差
pd.Timestamp( '2024-01-02 14:30:00' )+pd.Timedelta( '1 days 02:30:00' )
Timestamp('2024-01-03 17:00:00')
时序索引 接下来,我们看看日期做索引的情况
将日期作为索引创建时间序列:
import pandas as pd data = [ 1 , 2 , 3 , 4 , 5 ] dates = pd.date_range(start= '2024-01-01' , periods= 5 , freq= 'D' ) ts = pd.Series(data, index=dates) ts
2024-01-01 1 2024-01-02 2 2024-01-03 3 2024-01-04 4 2024-01-05 5 Freq: D, dtype: int64
其中,periods是时间序列的长度,freq是频率,这里设置为'D'表示每天。
时间序列的索引和切片: 使用日期进行索引:
import pandas as pd ts[ '2024-01-01' ]
1
使用日期范围进行切片:
import pandas as pd ts[ '2024-01-01' : '2024-01-05' ]
2024-01-01 1 2024-01-02 2 2024-01-03 3 2024-01-04 4 2024-01-05 5 Freq: D, dtype: int64
也可以使用切片操作对数据进行访问
import pandas as pd ts[ 1 : 4 ]
2024-01-02 2 2024-01-03 3 2024-01-04 4 Freq: D, dtype: int64
时间序列的重采样: 将时间序列从高频率转换为低频率:
import pandas as pd ts.resample( 'W' ).mean()
2024-01-07 3.0 Freq: W-SUN, dtype: float64
其中,'W'表示按周进行重采样,mean()表示计算每周的平均值。
时间序列的滚动计算: 计算滚动平均值:
import pandas as pd ts.rolling(window= 3 ).mean()
2024-01-01 NaN 2024-01-02 NaN 2024-01-03 2.0 2024-01-04 3.0 2024-01-05 4.0 Freq: D, dtype: float64
其中,window=3表示窗口大小为3,即计算每3个数据的平均值。
时间序列的时间偏移: 将时间序列向前或向后移动:
import pandas as pd ts.shift( 1 )
2024-01-01 NaN 2024-01-02 1.0 2024-01-03 2.0 2024-01-04 3.0 2024-01-05 4.0 Freq: D, dtype: float64
其中,1表示向后移动1个时间单位。
时间访问器dt 在 Pandas 中,可以使用 dt 访问器来访问时间戳或时间序列中的各个时间部分,例如年、月、日、小时、分钟、秒等。通过使用 dt 访问器,你可以方便地提取和操作时间信息。
下面是一些常用的 dt 访问器的示例:
import pandas as pd # 创建一个时间序列 timestamps = pd.Series(pd.date_range( '2023-01-01' , periods= 5 , freq= 'D' )) timestamps
0 2023-01-01 1 2023-01-02 2 2023-01-03 3 2023-01-04 4 2023-01-05 dtype: datetime64[ns]
# 提取年份 year = timestamps.dt.year year
0 2023 1 2023 2 2023 3 2023 4 2023 dtype: int64
# 提取月份 month = timestamps.dt.month month
0 1 1 1 2 1 3 1 4 1 dtype: int64
# 提取日期 day = timestamps.dt.day day
0 1 1 2 2 3 3 4 4 5 dtype: int64
# 提取小时 hour = timestamps.dt.hour hour
0 0 1 0 2 0 3 0 4 0 dtype: int64
# 提取分钟 minute = timestamps.dt.minute minute
0 0 1 0 2 0 3 0 4 0 dtype: int64
# 提取秒数 second = timestamps.dt.second second
0 0 1 0 2 0 3 0 4 0 dtype: int64
# 获取季度 quarter = timestamps.dt.quarter quarter
0 1 1 1 2 1 3 1 4 1 dtype: int64
# 获取周数 week = timestamps.dt.isocalendar().week week
0 52 1 1 2 1 3 1 4 1 Name: week, dtype: UInt32
# 获取星期几的名称 day_name = timestamps.dt.day_name() day_name
0 Sunday 1 Monday 2 Tuesday 3 Wednesday 4 Thursday dtype: object
# 获取该日期是一年中的第几天 day_of_year = timestamps.dt.dayofyear day_of_year
0 1 1 2 2 3 3 4 4 5 dtype: int64
# 获取该日期是一周中的第几天(星期一为1,星期日为7) day_of_week = timestamps.dt.dayofweek + 1 day_of_week
0 7 1 1 2 2
3 3 4 4 dtype: int64
# 获取该日期是一个月中的第几天 day_of_month = timestamps.dt.day day_of_month
0 1 1 2 2 3 3 4 4 5 dtype: int64
# 获取该日期所在月份的最后一天 end_of_month = timestamps.dt.daysinmonth end_of_month
0 31 1 31 2 31 3 31 4 31 dtype: int64
时长转化 import pandas as pd # 创建时间戳序列 ts = pd.Series(pd.to_timedelta(np.arange( 10 ),unit= 'm' )) ts
0 0 days 00:00:00 1 0 days 00:01:00 2 0 days 00:02:00 3 0 days 00:03:00 4 0 days 00:04:00 5 0 days 00:05:00 6 0 days 00:06:00 7 0 days 00:07:00 8 0 days 00:08:00 9 0 days 00:09:00 dtype: timedelta64[ns]
# 提取时间戳中的秒数 seconds = ts.dt.seconds seconds
0 0 1 60 2 120 3 180 4 240 5 300 6 360 7 420 8 480 9 540 dtype: int64
seconds = ts.dt.to_pytimedelta() seconds
array([datetime.timedelta(0), datetime.timedelta(seconds=60), datetime.timedelta(seconds=120), datetime.timedelta(seconds=180), datetime.timedelta(seconds=240), datetime.timedelta(seconds=300), datetime.timedelta(seconds=360), datetime.timedelta(seconds=420), datetime.timedelta(seconds=480), datetime.timedelta(seconds=540)], dtype=object)
以上是Pandas针对时间序列的一些常用操作和示例代码
第3节 11 Pandas 窗口数据 Pandas提供了窗口函数(Window Functions)用于在数据上执行滑动窗口操作,可以对数据进行滚动计算、滑动统计等操作。下面是一些常用的窗口函数:
滚动计算函数: 移动平均值(Moving Average):
import pandas as pd data = { 'column' : [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ]} df = pd.DataFrame(data) df
df[ 'MA' ] = df[ 'column' ].rolling(window= 3 ).mean() df
其中,window=3表示窗口大小为3,即计算每3个数据的平均值。
滚动求和(Rolling Sum):
import pandas as pd df[ 'Sum' ] = df[ 'column' ].rolling(window= 5 ).sum() df
其中,window=5表示窗口大小为5,即计算每5个数据的和。
滑动统计函数: 滑动最大值(Rolling Maximum):
import pandas as pd df[ 'Max'
] = df[ 'column' ].rolling(window= 7 ).max() df
其中,window=7表示窗口大小为7,即计算每7个数据的最大值。
滑动最小值(Rolling Minimum):
import pandas as pd df[ 'Min' ] = df[ 'column' ].rolling(window= 7 ).min() df
其中,window=7表示窗口大小为7,即计算每7个数据的最小值。
滑动标准差(Rolling Standard Deviation):
import pandas as pd df[ 'Std' ] = df[ 'column' ].rolling(window= 5 ).std() df
其中,window=5表示窗口大小为5,即计算每5个数据的标准差。
自定义窗口函数: 可以使用rolling().apply()方法来应用自定义的窗口函数:
import pandas as pd def custom_function (data) : # 自定义的窗口函数逻辑 return max(data) - min(data) df[ 'Result' ] =df[ 'column' ].rolling(window= 3 ).apply(custom_function) df
其中,custom_function是自定义的窗口函数,data是窗口中的数据,result是窗口函数的计算结果。 以上是Pandas窗口函数的一些常用操作和示例代码。需要注意的是,在使用窗口函数时,需要根据实际需求选择合适的窗口大小和窗口函数,并确保数据的顺序和窗口大小的一致性。
Pandas 数据读写 Pandas提供了多种读取数据的方法,包括读取CSV、Excel、SQL数据库等。
CSV 写出 csv
文件
import pandas as pd import numpy as np data = np.random.rand( 10 , 10 ) # 生成一个10行10列的随机数矩阵 columns = [ 'col' + str(i)
for i in range( 10 )] # 列名为col0, col1, ..., col9 df = pd.DataFrame(data, columns=columns) df
df.to_csv( './output/foo.csv' ) # 请注意需要在你的代码文件夹目录下建一个\output 文件夹才能写入
读入刚刚写出的文件
pd.read_csv( './output/foo.csv' )
EXCEL 写出 excel
文件
df.to_excel( './output/foo.xlsx' , sheet_name= 'Sheet1' ,index = None )
读取 excel
文件
pd.read_excel( './output/foo.xlsx' , 'Sheet1' , index_col= None , na_values=[ 'NA' ])
HDF 写出 hdf
文件
df.to_hdf( './output/foo.h5' , 'df' )
读入刚刚写出的文件
pd.read_hdf( './output/foo.h5' , 'df' ).head()
MySQL 写出到mysql里
from sqlalchemy import create_engine import pandas as pd
mysql_engine=create_engine( "mysql+pymysql://root:password@localhost/test" ) df.to_sql(pust_table_name,mysql_engine,if_exists= 'replace' ,index = False ) # 注意 mysql_engine一定要正确配置
读入刚刚写出的文件
df = pd.read_sql( """ select a,b from pust_table_name; """ ,mysql_engine) # 再次强调,mysql_engine一定要正确配置,实在有问题可以私信 aiu_cda df
第3节 13 Pandas 表格样式 Pandas 表格样式 Pandas 的样式是一个可视化的方法,像Excel一样对特定数据进行加粗、标红、背景标黄等,为了让数据更加清晰醒目,突出数据的逻辑和特征。
假如我们有这样一个 DataFrame
,我们需要通过表格样式给它做各种标注:
#读取数据 import pandas as pd import numpy as np df = df = pd.DataFrame( { 'A' : [ '孙云' , '郑成' , '冯敏' , '王忠' , '郑花' , '孙华' , '赵白' , '王花' , '黄成' , '钱明' , '孙宇' ], 'B' : [ 79 , 70 , 39 , 84 , 87 , 26 , 29 , 47 , 32 , 22 , 99 ], 'C' : [ 28 , 77 , 84 , 26 , 29 , 47 , 32 , 22 , 99 , 76 , 44 ], 'D' : [ 18 , 53 , 78 , 4 , 36 , 88 , 79 , 47 , 54 , 25 , 14 ]}) df
字体颜色 首先来看一个对文字标注颜色的例子:eg.我们想把成绩超过80的分数用红色标注出来
我们需要先定义一个函数,根据条件返回不同的颜色
def color_negative_red (val) : color = 'red' if val > 80 else
'black' return 'color: %s' % color
应用这个自定义函数后就可以得到:
df.set_index( 'A' ).style.applymap(color_negative_red)
背景高亮 接着 eg. 我们假设有学生没有去考试,想看看哪些学生没有考试,把这部分进行背景高亮显示
数据如下:
df1 = df.copy() df1.iloc[ 1 , 1 ] = np.NaN df1.iloc[ 2 , 1 ] = np.NaN df1
换句话说,就是用背景高亮标记出空值,应用 .highlight_null()
即可将空值高亮显示,同时用 null_color
参数可以指定该高亮的颜色。
#把空值设置高亮 df1.style.highlight_null(null_color = 'blue' #修改颜色 )
极值背景高亮 接着我们想看看 eg. 标记出每个科目的最高分数
换句话说,需要查找DataFrame每一列的最大值,通过 highlight_max() 方法用于将最大值高亮显示,并通过color参数修改高亮颜色
#设置极大值高亮 df.set_index( 'A' ).style.highlight_max(color = 'red' #修改颜色 )
通过 highlight_min()
方法可以将最小值高亮显示
df.set_index( 'A' ).style.highlight_min(color = 'yellow'
#修改颜色 )
同时显示极大值和极小值,并使用指定颜色:通过 highlight_min()
方法和 highlight_max()
方法再指定一下颜色即可
df.set_index( 'A' ).style.highlight_min(color = 'green' ).highlight_max(color = 'red' )
横向对比 再来看看横向对比的例子 eg. 需要标记出每个学生的单科最高分数: 通过参数 axis ,横向对比大小,并把最大值进行高亮显示即可
df.set_index( 'A' ).style.highlight_max(axis = 1 )
同样的,也可以通过参数 subset ,选定一列对最大值进行高亮显示
#指定列进行比较 df.set_index( 'A' ).style.highlight_max(subset = [ 'B' ])
背景渐变 eg. 用不同的颜色来标注成绩,背景颜色越深,成绩越高
通过调用 background_gradient() 方法,从而创建一个渐变的背景效果。
df.style.background_gradient()
同样地,针对单个列,指定颜色系列如下:
df.style.background_gradient(subset = [ 'B' ],cmap = 'BuGn' )
刚才我们是默认颜色渐变的范围了,接着我们来看如何指定颜色渐变的范围,来展现成绩的高低
通过调用 background_gradient()
方法,用了两个参数 low=0.5 和 high=0 表示渐变的起始值和结束值
#低百分比和高百分比范围,更换颜色时避免使用所有色域 df.style.background_gradient(low = 0.5 ,high = 0 )
接着我们看看如何对特定范围内的值就行标注
eg. 假如需要把60分以上的分数用颜色标注出来
通过参数 vmin 和参数 vmax 设置渐变的最小值和最大值,就可以展现出来。
df.style.background_gradient(vmin = 60 ,vmax = 100 )
eg. 用此次考试成绩表,添加标题
通过.set_caption() 方法为DataFrame 即可设置标题。
#添加标题 df.style.set_caption( "三年级二班学生成绩表" )
通过以上内容的学习,我们快速学习Pandas样式的基本操作,接下来,再用两个案例详细说明一下
案例一:将科目分数小于60的值,用红色进行高亮显示
#将学生没有及格的科目标记为红色 df.style.applymap( lambda x: 'background-color:red' if x< 60 else '' , subset = pd.IndexSlice[:,[ 'B' , 'C' , 'D' ]])
案例二:标记总分低于120分的分数
将每个学生的分数,进行加总和计算平均数,并保留两位小数,把分数低于120的学生,用红色进行标记即可
#通过使用.assign() 来计算学生三门课程的总分和平均值 (df.set_index( 'A' ).assign(sum_s = df.set_index( 'A' ).sum(axis = 1 )) .style.applymap( lambda x: 'background-color:red'
if x< 120 else '' ,subset = pd.IndexSlice[:,[ 'sum_s' ]]) .format({ 'avg' : "{:.2f}" }))
第3节 14 Pandas 可视化 一图胜千言 A picture is worth a thousand words.
常见的可视化图有如下几种:
#用于处理解决中文乱码问题和负号问题。 import matplotlib.pyplot as plt plt.rcParams[ 'font.sans-serif' ] = [ 'SimHei' ] #显示中文问题 plt.rcParams[ 'axes.unicode_minus' ] = False #显示负号
line:折线图 折线图一般用于描述数据的 趋势 :
import pandas as pd import numpy as np import random df = pd.DataFrame({ 'A' : [ 'a' , 'b' , 'c' , 'd' , 'e' , 'f' , 'g' , 'h' , 'i' ], 'B' : [ 'L' , 'L' , 'M' , 'L' , 'M' , 'M' , 'M' , 'L' , 'L' ], 'C' : [ 107 , 177 , 139 , 38 , 52 ,
38 , 87 , 38 , 56 ], 'D' : [ 22 , 59 , 38 , 59 , 59 , 82 , 89 , 48 , 88 ]}).set_index( 'A' ) df
一组数据的折线图如下:
df[ 'C' ].plot.line( )
再来看一下两组数据的折线图
df[[ 'C' , 'D' ]].plot.line()
pie:饼图 饼图一般用于展示数据的占比关系
np.random.seed( 123 ) df1 = pd.Series( 3 *np.random.rand( 4 ),index = [ 'a' , 'b' , 'c' , 'd' ],name = '占比' ) df1
a 0.683925 b 0.707985 c 1.646343 d 1.803777 Name: 占比, dtype: float64
看看以上四个数据的占比情况
df1.plot.pie()
bar:柱状图 柱状图一般用于各种类型数据的对比。
一组数据的柱状图
df[ 'C' ].plot.bar()
两组数据的柱状图
df[[ 'C' , 'D' ]].plot.bar()
横向柱状图
df.plot.barh()
其他几种柱状图
df.assign(a = df.C- 70 ).plot.bar()
df.plot.bar(stacked = True )
df.plot.barh(stacked = True )
df.head( 5 ).plot.barh(stacked = True ,colormap= 'cool' )
hist:直方图 直方图用于展示数据的分布情况
np.random.seed( 123 ) df2 = pd.DataFrame({
'a' :np.random.randn( 1000 )+ 1 , 'b' :np.random.randn( 1000 ), 'c' :np.random.randn( 1000 ) -1 }, columns = [ 'a' , 'b' , 'c' ]) df2
1000 rows × 3 columns
先来看下一组数据的直方图
df2[ 'a' ].plot.hist()
多组数据的直方图
df2.plot.hist()
指定分箱数量的直方图
#堆叠,指定分箱数量 df2.plot.hist(stacked = True ,bins = 30 )
box:箱型图 箱型图用于展示数据的分布、识别异常值以及比较不同组之间的差异。
一组数据的箱型图
df.boxplot( 'C' )
再来看看用两列数据来画两个箱型图
import pandas as pd import numpy as np import random
df = pd.DataFrame({ 'A' : [ 'a' , 'b' , 'c' , 'd' , 'e' , 'f' , 'g' , 'h' , 'i' ], 'B' : [ 'L' , 'L' , 'M' , 'L' , 'M' , 'M' , 'M' , 'L' , 'L' ], 'C' : [ 107 , 177 , 139 , 38 , 52 , 38 , 87 , 38 , 56 ], 'D' : [ 22 , 59 , 38 , 59 , 59 , 82 , 89 , 48 , 88 ]}).set_index( 'A' ) df
df.boxplot([ 'C' , 'D' ])
横向箱线图
df.boxplot([ 'C' , 'D' ],vert = False )
area:面积图 面积图是一种常见且有效的数据可视化工具,用于展示数据的趋势、比较不同组之间的差异以及理解数据的部分与整体关系。广泛应用于统计学、经济学、市场调研、环境科学等领域,并为数据分析和决策提供了重要的支持。
np.random.seed( 123 ) df4 = pd.DataFrame(np.random.rand( 10 , 4 ),columns = [ 'a' , 'b' , 'c' , 'd' ]) df4
一组数据的面积图
df4[ 'a' ].plot.area()
多组数据的面积图
df4[[ 'a' , 'b' , 'c' , 'd' ]].plot.area()
scatter:散点图 散点图用于发现变量之间的关系、探索异常情况、进行聚类分析以及支持预测和模型建立
np.random.seed( 123 ) df = pd.DataFrame(np.random.randn( 10 , 2 ),columns = [ 'B' , 'C' ]).cumsum() df
看一下这两列数据的散点图
df.assign(avg = df.mean( 1 )).plot.scatter(x= 'C' ,y = 'B' )
至此,Python基础部分pandas常用的内容就告一段落了。
致谢
《Python数据分析极简入门》图文系列教程的写作过程中参考了诸多经典书籍,包括:
Wes McKinney 的《利用Python做数据分析》;
小甲鱼老师的《零基础入门学习Python》;
李庆辉老师的 《深入浅出Pandas》;
在此一并感谢以上内容的作者!
怕什么真理无穷,进一寸有一寸的欢喜。你每每往前进一寸,你的天空,便有一片新的明朗。你便会有一片新的开阔。诸位加油,我们下个系列见!