2020-08-04--Pandas-02--常用基本功能
常用的基本功能
当我们构建好了 Series 和 DataFrame 之后,我们会经常使用哪些功能呢?来跟我看看吧。引用上一章节中的场景,我们有一些用户的的信息,并将它们存储到了 DataFrame 中。
因为大多数情况下 DataFrame 比 Series 更为常用,所以这里以 DataFrame 举例说明,但实际上很多常用功能对于 Series 也适用。
创建DataFrame对象数据:
import pandas as pd
import numpy as np
index = pd.Index(data=["Tom", "Bob", "Mary", "James"], name="name")
data = {
"age": [18, 30, 25, 40],
"city": ["BeiJing", "ShangHai", "GuangZhou", "ShenZhen"],
"sex": ["male", "male", "female", "male"]
}
user_info = pd.DataFrame(data=data, index=index)
print(user_info)
这是我们自己创建的数据,但是如果在一般情况下我们是不知道数据的具体情况的,所以我们需要值数据的一些具体信息。
1.info()
一般拿到数据,我们第一步需要做的是了解下数据的整体情况,可以使用 info 方法来查看。
# 查看数据的整体情况
print(user_info.info())
# <class 'pandas.core.frame.DataFrame'>
# Index: 4 entries, Tom to James
# Data columns (total 3 columns):
# # Column Non-Null Count Dtype
# --- ------ -------------- -----
# 0 age 4 non-null int64
# 1 city 4 non-null object
# 2 sex 4 non-null object
# dtypes: int64(1), object(2)
# memory usage: 128.0+ bytes
# None
2.head(num)和tail(num)
如果我们的数据量非常大,我想看看数据长啥样,我当然不希望查看所有的数据了,这时候我们可以采用只看头部的 n 条或者尾部的 n 条。查看头部的 n 条数据可以使用 head 方法,查看尾部的 n 条数据可以使用 tail 方法。
# 查看前两条数据
p = user_info.head(2)
print(p)
# age city sex
# name
# Tom 18 BeiJing male
# Bob 30 ShangHai male
3.shape和T()
此外,Pandas 中的数据结构都有 ndarray 中的常用方法和属性,如通过 .shape 获取数据的形状,通过 .T 获取数据的转置。
# 获取数据的形状
print(user_info.shape) # (4, 3)
# 获取数据的转置
print(user_info.T)
# name Tom Bob Mary James
# age 18 30 25 40
# city BeiJing ShangHai GuangZhou ShenZhen
# sex male male female male
print(type(user_info.T)) # <class 'pandas.core.frame.DataFrame'>
4.原有数据
如果我们想要通过 DataFrame 来获取它包含的原有数据,可以通过 .values 来获取,获取后的数据类型其实是一个 ndarray。
# 获取其中的原有数据,返回一个ndarray
print(user_info.values)
# [[18 'BeiJing' 'male']
# [30 'ShangHai' 'male']
# [25 'GuangZhou' 'female']
# [40 'ShenZhen' 'male']]
print(type(user_info.values))
# <class 'numpy.ndarray'>
最后也可以通过切片获取想要的数据。--values[1:3]
描述与统计
1.max(),min(),mean()、quantile()、sum()
有时候我们获取到数据之后,想要查看下数据的简单统计指标(最大值、最小值、平均值、中位数等),比如想要查看年龄的最大值,如何实现呢?
可以直接对 age 这一列调用 max方法即可。
# 获取年龄的最大值
print(user_info.age.max()) # 40
类似的,通过调用 min、mean、quantile、sum 方法可以实现最小值、平均值、中位数以及求和。可以看到,对一个 Series 调用 这几个方法之后,返回的都只是一个聚合结果。
2.cumsum
cumsum,看名字就发现它和 sum 方法有关系,事实上确实如此,cumsum 也是用来求和的,不过它是用来累加求和的,也就是说它得到的结果与原始的 Series或 DataFrame 大小相同。
# 累加
print(user_info.age.cumsum())
# name
# Tom 18
# Bob 48
# Mary 73
# James 113
# Name: age, dtype: int64
print(type(user_info.age.cumsum()))
# <class 'pandas.core.series.Series'>
可以看到,cummax 最后的结果就是将上一次求和的结果与原始当前值求和作为当前值。这话听起来有点绕。举个例子,上面的 73 = 48 + 25。cumsum 也可以用来操作字符串类型的对象。
print(user_info.sex.cumsum())
# name
# Tom male
# Bob malemale
# Mary malemalefemale
# James malemalefemalemale
# Name: sex, dtype: object
3.整体统计
虽然说常见的各种统计值都有对应的方法,如果我想要得到多个指标的话,就需要调用多次方法,是不是显得有点麻烦呢?
Pandas 设计者自然也考虑到了这个问题,想要一次性获取多个统计指标,只需调用 describe方法即可。
print(user_info.describe())
# age
# count 4.000000 # 统计age有多少行
# mean 28.250000 # 统计age的均值
# std 9.251126 # 统计age的均方差
# min 18.000000 # 最小值
# 25% 23.250000 # 25%的值
# 50% 27.500000 # 50%的值
# 75% 32.500000 # 75%的值
# max 40.000000 # 最大值
print(type(user_info.describe()))
# <class 'pandas.core.frame.DataFrame'>
可以看到,直接调用 describe 方法后,会显示出数字类型的列的一些统计指标,如 总数、平均数、标准差、最小值、最大值、25%/50%/75% 分位数。如果想要查看非数字类型的列的统计指标,可以设置 include=["object"] 来获得。
print(user_info.describe(include=['object']))
# city sex
# count 4 4
# unique 4 2
# top ShangHai male
# freq 1 3
上面的结果展示了非数字类型的列的一些统计指标:总数,去重后的个数、最常见的值、最常见的值的频数。
4.次数统计
此外,如果我想要统计下某列中每个值出现的次数,如何快速实现呢?调用 value_counts 方法快速获取 Series 中每个值出现的次数。
# 统计sex出现的次数
print(user_info.sex.value_counts())
# male 3
# female 1
# Name: sex, dtype: int64
5.最大值/最小值的索引
如果想要获取某列最大值或最小值对应的索引,可以使用 idxmax 或 idxmin 方法完成。
# 如果想要获取age最大值或最小值对应的索引
print(user_info.age.idxmax())
# James type:str
print(user_info.age.idxmin())
# Tom
离散化
有时候,我们会碰到这样的需求,想要将年龄进行离散化(分桶),直白来说就是将年龄分成几个区间,这里我们想要将年龄分成 3 个区间段。就可以使用 Pandas 的 cut 方法来完成。
print(user_info)
# age city sex
# name
# Tom 18 BeiJing male
# Bob 30 ShangHai male
# Mary 25 GuangZhou female
# James 40 ShenZhen male
# 将age自动切分为三段
cate_age = pd.cut(user_info.age,3)
print(cate_age)
# name
# Tom (17.978, 25.333]
# Bob (25.333, 32.667]
# Mary (17.978, 25.333]
# James (32.667, 40.0]
# Name: age, dtype: category
# Categories (3, interval[float64]): [(17.978, 25.333] < (25.333, 32.667] < (32.667, 40.0]]
print(type(cate_age)) # <class 'pandas.core.series.Series'>
可以看到, cut 自动生成了等距的离散区间,如果自己想定义也是没问题的。
# 将age切分为指定区间段
cate_age = pd.cut(user_info.age,[1,18,30,50] )
print(cate_age)
# name
# Tom (1, 18]
# Bob (18, 30]
# Mary (18, 30]
# James (30, 50]
# Name: age, dtype: category
# Categories (3, interval[int64]): [(1, 18] < (18, 30] < (30, 50]]
有时候离散化之后,想要给每个区间起个名字,可以指定 labels 参数。
# 设置label属性---区间名
cate_age = pd.cut(user_info.age,[1,18,30,50],labels=["childhood", "youth", "middle"])
print(cate_age)
# name
# Tom childhood
# Bob youth
# Mary youth
# James middle
# Name: age, dtype: category
# Categories (3, object): ['childhood' < 'youth' < 'middle']
除了可以使用 cut 进行离散化之外,qcut 也可以实现离散化。cut 是根据每个值的大小来进行离散化的,qcut 是根据每个值出现的次数来进行离散化的。
# qcut:根据值出现的次数进行离散化的
cate_age = pd.qcut(user_info.age,3)
print(cate_age)
# name
# Tom (17.999, 25.0]
# Bob (25.0, 30.0]
# Mary (17.999, 25.0]
# James (30.0, 40.0]
# Name: age, dtype: category
# Categories (3, interval[float64]): [(17.999, 25.0] < (25.0, 30.0] < (30.0, 40.0]]
排序功能
在进行数据分析时,少不了进行数据排序。Pandas 支持两种排序方式:按轴(索引或列)排序和按实际值排序。
先来看下按索引排序:sort_index() 方法默认是按照索引(行标识)进行正序排的。
print(user_info)
# age city sex
# name
# Tom 18 BeiJing male
# Bob 30 ShangHai male
# Mary 25 GuangZhou female
# James 40 ShenZhen male
# 按照索引正排
user_sort = user_info.sort_index()
print(user_sort)
# age city sex
# name
# Bob 30 ShangHai male
# James 40 ShenZhen male
# Mary 25 GuangZhou female
# Tom 18 BeiJing male
- 设置参数axis,ascending
- axis为0时,对行标进行排序,为1时,对列标进行排序,默认为0
- ascending:为True时,正序,为False时,倒序,默认为True
如果想要按照列进行倒序排,可以设置参数 axis=1 和 ascending=False。
# 设置参数axis,ascending
# axis为0时,对行标进行排序,为1时,对列标进行排序,默认为0
# ascending:为True时,正序,为False时,倒序,默认为True
user_sort_col_F = user_info.sort_index(axis=1,ascending=False)
print(user_sort_col_F)
# sex city age
# name
# Tom male BeiJing 18
# Bob male ShangHai 30
# Mary female GuangZhou 25
# James male ShenZhen 40
可以发现列的顺序变了。
如果想要实现按照实际值来排序,例如想要按照年龄排序,如何实现呢?
使用 sort_values 方法,设置参数 by="age" 即可。
# 按照实际值来排序,例如想要按照年龄正序排序
ageSort = user_info.sort_values(by='age')
print(ageSort)
# age city sex
# name
# Tom 18 BeiJing male
# Mary 25 GuangZhou female
# Bob 30 ShangHai male
# James 40 ShenZhen male
当两条数据中的某个值一样时,那么就会存在优先级的问题。
有时候我们可能需要按照多个值来排序,例如:按照年龄和城市来一起排序,可以设置参数 by 为一个 list 即可。
注意:list 中每个元素的顺序会影响排序优先级的。
# 按照age和城市两个标识进行排序
ageCity_sort = user_info.sort_values(by=["age", "city"])
print(ageCity_sort)
# age city sex
# name
# Tom 18 BeiJing male
# Bob 18 ShangHai male
# Mary 25 GuangZhou female
# James 40 ShenZhen male
将原数据中修改两个一样的age,进行测试。
一般在排序后,我们可能需要获取最大的n个值或最小值的n个值,我们可以使用 nlargest 和 nsmallest 方法来完成,这比先进行排序,再使用 head(n) 方法快得多。
# 获取的数据中某项值最大和最小的几条数据
head = user_info.age.nlargest(2)
print(head)
# name
# James 40
# Mary 25
# Name: age, dtype: int64
print(type(head)) # <class 'pandas.core.series.Series'>
tail = user_info.age.nsmallest(2)
print(tail)
# name
# Tom 18
# Bob 18
# Name: age, dtype: int64
函数应用
虽说 Pandas 为我们提供了非常丰富的函数,有时候我们可能需要自己定制一些函数,并将它应用到 DataFrame 或 Series。常用到的函数有:map、apply、applymap。
这些函数的作用就是对数据集中的数据通过该函数的参数(可以是自定义函数名,lambda表达式,也可以是字典映射)实现转换功能。
1.map()
map 是 Series 中特有的方法,通过它可以对 Series 中的每个元素实现转换,也就是说map值针对于数据的某一列或行进行操作替换。
如果我想通过年龄判断用户是否属于中年人(30岁以上为中年),通过 map 可以轻松搞定它。
print(user_info)
# age city sex
# name
# Tom 18 BeiJing male
# Bob 30 ShangHai male
# Mary 25 GuangZhou female
# James 40 ShenZhen male
# map
c = user_info.age.map(lambda x: "yes" if x >= 30 else "no")
print(c)
# name
# Tom no
# Bob no
# Mary no
# James yes
# Name: age, dtype: object
当函数复杂一些时,可以在外边定义函数,将函数名传进map函数中,也可以通过字典映射的方式。
比如,我想要通过城市来判断是南方还是北方,我可以这样操作。
# map,字典映射的方式
city_map = {
"BeiJing": "north",
"ShangHai": "south",
"GuangZhou": "south",
"ShenZhen": "south"
}
# map,闯入字典映射的方式
c = user_info.city.map(city_map)
print(c)
# name
# Tom north
# Bob south
# Mary south
# James south
# Name: city, dtype: object
2.apply()
apply 方法既支持 Series,也支持 DataFrame,在对 Series 操作时会作用到每个值上,在对 DataFrame 操作时会作用到所有行或所有列(通过 axis 参数控制)。
- axis=0:列,axis=1,行
# apply(),对Series来书,apply()和map()一样
c = user_info.age.apply(lambda x: "yes" if x >= 30 else "no")
print(c)
# name
# Tom no
# Bob no
# Mary no
# James yes
# Name: age, dtype: object
# 对于DataFrame来说的apply()
# 将每列的数据闯进函数中,取出每列的最大值
c = user_info.apply(lambda x: x.max(), axis=0)
print(c)
# age 40
# city ShenZhen
# sex male
# dtype: object
3.applymap()
applymap 方法针对于 DataFrame,它作用于 DataFrame 中的每个元素,它对 DataFrame 的效果类似于 apply 对 Series 的效果。
applymap():将数据中所有除索引和列标识之外的所有数据项循环加到函数中进行操作。
print(user_info)
# age city sex
# name
# Tom 18 BeiJing male
# Bob 18 ShangHai male
# Mary 25 GuangZhou female
# James 40 ShenZhen male
# 将数据中所有数据进行相应的替换
def func(data):
if type(data) == int:
if data>=18 and data<=30:
return '青年人'
elif data>=30 and data<=45:
return '中年人'
elif type(data) == str:
if data == 'BeiJing':
return '北京'
elif data == 'ShangHai':
return '上海'
elif data == 'GuangZhou':
return '广州'
elif data == 'ShenZhen':
return '深圳'
elif data == 'male':
return '男'
elif data == 'female':
return '女'
c= user_info.applymap(func)
print(c)
# name
# Tom 青年人 北京 男
# Bob 青年人 上海 男
# Mary 青年人 广州 女
# James 中年人 深圳 男
在函数中使用字典映射,字典映射只能判断值。不能判断区间。
def func(data):
dic = {
'male':'男',
'female':'女',
'BeiJing':'北京',
'ShangHai':'上海',
'ShenZhen':'深圳',
'GuangZhou':'广州',
}
if isinstance(data,int):
if data>=18 and data<=30:
return '青年人'
elif data>=30 and data<=45:
return '中年人'
elif isinstance(data,str):
return dic[data]
c= user_info.applymap(func)
print(c)
# name
# Tom 青年人 北京 男
# Bob 青年人 上海 男
# Mary 青年人 广州 女
# James 中年人 深圳 男
修改列/索引名称
rename()
使用user_info.rename(columns={'oldname':'newname',...},index={'old':'new',...})
print(user_info)
# age city sex
# name
# Tom 18 BeiJing male
# Bob 30 ShangHai male
# Mary 25 GuangZhou female
# James 40 ShenZhen male
# 修改列标识和索引(行标识)
user_info = user_info.rename(columns={'age':'Age','city':'City','sex':'Sex'},index={'Tom':'John'})
print(user_info)
# Age City Sex
# name
# John 18 BeiJing male
# Bob 18 ShangHai male
# Mary 25 GuangZhou female
# James 40 ShenZhen male
类型操作
astype ()
单独对某一列进行转换数据类型的话,可以通过 astype 来完成。
print(user_info)
# age city sex
# name
# Tom 18 BeiJing male
# Bob 30 ShangHai male
# Mary 25 GuangZhou female
# James 40 ShenZhen male
# 转换数据类型
user_type= user_info.age.astype(float)
print(user_type)
# name
# Tom 18.0
# Bob 18.0
# Mary 25.0
# James 40.0
# Name: age, dtype: float64
print(type(user_type))
# <class 'pandas.core.series.Series'>
有时候会涉及到将 object 类型转为其他类型,常见的有
- 数字:
pd.to_numeric(Series)
- 日期:
pd.to_datetime(Series)
- 时间差:
pd.to_timedelta(Series)
例子:
将age转为数字类型:
# 转为数字类型
a = pd.to_numeric(user_info.age)
print(a)
# name
# Tom 18
# Bob 18
# Mary 25
# James 40
# Name: age, dtype: int64
print(type(a))
# # <class 'pandas.core.series.Series'>
实例:
这里给这些用户都添加一些关于身高的信息。
user_info["height"] = ["178", "168", "178", "180cm"]
print(user_info)
# age city sex height
# name
# Tom 18 BeiJing male 178
# Bob 18 ShangHai male 168
# Mary 25 GuangZhou female 178
# James 40 ShenZhen male 180cm
现在将身高这一列转为数字,很明显,180cm 并非数字,为了强制转换,我们可以传入 errors 参数,这个参数的作用是当强转失败时的处理方式。
默认情况下,
- errors='raise',这意味着强转失败后直接抛出异常,
- errors='coerce'可以在强转失败时将有问题的元素赋值为 pd.NaT(对于datetime和timedelta)或 np.nan(数字)。
- errors='ignore' 可以在强转失败时返回原有的数据。
hei = pd.to_numeric(user_info.height)
print(hei)
# 直接报错
hei = pd.to_numeric(user_info.height,errors='coerce')
print(hei)
# name
# Tom 178.0
# Bob 168.0
# Mary 178.0
# James NaN
# Name: height, dtype: float64
hei = pd.to_numeric(user_info.height,errors='ignore')
print(hei)
# name
# Tom 178
# Bob 168
# Mary 178
# James 180cm
# Name: height, dtype: object