Pandas(二)

Pandas 学习系列(二)

常用的基本功能

以 DataFrame 的操作为例,大部分也适用于 Series。

1
2
import numpy as np
import pandas as pd
1
2
3
4
5
6
7
8
9
10
index = pd.Index(data=['Tom', 'Bob', 'Mary', 'James'], name='username')

data = {
'age': [18, 30, 25, 40],
'city': ['Shanghai', 'Beijing', 'Guangzhou', 'Shenzhen'],
'gender': ['male', 'male', 'female', 'male'],
}

user_info = pd.DataFrame(data=data, index=index)
user_info
age city gender
username
Tom 18 Shanghai male
Bob 30 Beijing male
Mary 25 Guangzhou female
James 40 Shenzhen male

整体情况

首先,我们可以通过 info 方法查看数据的整体情况:

1
user_info.info()
<class 'pandas.core.frame.DataFrame'>
Index: 4 entries, Tom to James
Data columns (total 3 columns):
age       4 non-null int64
city      4 non-null object
gender    4 non-null object
dtypes: int64(1), object(2)
memory usage: 128.0+ bytes

头尾数据

数据量巨大的时候,可以像操作大文本一样使用 headtail 类似的操作:

1
user_info.head(2)
age city gender
username
Tom 18 Shanghai male
Bob 30 Beijing male
1
user_info.tail(2)
age city gender
username
Mary 25 Guangzhou female
James 40 Shenzhen male

变形

.shape 可以获取数据的形状,.T 可以对数据转置:

1
user_info.shape
(4, 3)
1
user_info.T

|username|Tom|Bob|Mary|James|
|age|18|30|25|40|
|city|Shanghai|Beijing|Guangzhou|Shenzhen|
|gender|male|male|female|male|

像字典一样,可以使用 .values 获取数据内容:

1
user_info.values
array([[18, 'Shanghai', 'male'],
       [30, 'Beijing', 'male'],
       [25, 'Guangzhou', 'female'],
       [40, 'Shenzhen', 'male']], dtype=object)

可见是 ndarray 类型。

当然也有 keys()items() 方法(values 只有属性):

1
user_info.keys()
Index(['age', 'city', 'gender'], dtype='object')
1
2
3
4
5
for key, value in user_info.items():
print(key)
print('-----')
print(value)
print('-----')
age
-----
username
Tom      18
Bob      30
Mary     25
James    40
Name: age, dtype: int64
-----
city
-----
username
Tom       Shanghai
Bob        Beijing
Mary     Guangzhou
James     Shenzhen
Name: city, dtype: object
-----
gender
-----
username
Tom        male
Bob        male
Mary     female
James      male
Name: gender, dtype: object
-----

描述统计

自带常用的数据统计指标(最大值、最小值、平均值、中位数等)

1
2
# 最大年龄
user_info.age.max()
40
1
2
# 最小年龄
user_info.age.min()
18
1
2
# 平均年龄
user_info.age.mean()
28.25
1
2
# 年龄总和
user_info.age.sum()
113
1
2
# 年龄中位数
user_info.age.median()
27.5
1
2
# 年龄方差
user_info.age.var()
85.58333333333333
1
2
# 年龄标准差
user_info.age.std()
9.251126057585278
1
user_info.age.quantile()
27.5

如果只是想粗略看一下各项描述指数,可以直接使用 describe() 方法:

1
user_info.age.describe()
count     4.000000
mean     28.250000
std       9.251126
min      18.000000
25%      23.250000
50%      27.500000
75%      32.500000
max      40.000000
Name: age, dtype: float64

这些统计方法可以直接在 DataFrame 上使用:

1
user_info.median()
age    27.5
dtype: float64
1
user_info.max()
age             40
city      Shenzhen
gender        male
dtype: object
1
user_info.describe()
age
count 4.000000
mean 28.250000
std 9.251126
min 18.000000
25% 23.250000
50% 27.500000
75% 32.500000
max 40.000000

如果想要对非数字类型的数据也统计,可以设置 include=['object'] 来获得:

1
user_info.describe(include=['object', 'int'])
age city gender
count 4.000000 4 4
unique NaN 4 2
top NaN Shenzhen male
freq NaN 1 3
mean 28.250000 NaN NaN
std 9.251126 NaN NaN
min 18.000000 NaN NaN
25% 23.250000 NaN NaN
50% 27.500000 NaN NaN
75% 32.500000 NaN NaN
max 40.000000 NaN NaN

离散化

对于一堆数据,我们有时候想分成几个区间去统计,这时候可以使用 Pandas 的 cut() 方法完成:

1
pd.cut(user_info.age, 3)
username
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]]

默认会自动化分成等距的几个区间段,我们也可以自己指定离散区间:

1
pd.cut(user_info.age, [1, 18, 30, 50])
username
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]]

例外还可以给每个离散区间取个名字:

1
pd.cut(user_info.age, [1, 18, 30, 50], labels=['childhood', 'youth', 'middle'])
username
Tom      childhood
Bob          youth
Mary         youth
James       middle
Name: age, dtype: category
Categories (3, object): [childhood < youth < middle]

排序

对于数据的处理怎么能少了排序呢。排序有两种,一种是按照索引(横纵两种)排序,还有就是按照实际的数据值排序.

一、按索引排序(默认正序):

1
2
# 按照索引排序
user_info.sort_index()
age city gender
username
Bob 30 Beijing male
James 40 Shenzhen male
Mary 25 Guangzhou female
Tom 18 Shanghai male
1
2
# 按照列索引排序(反序)
user_info.sort_index(axis=1, ascending=False)
gender city age
username
Tom male Shanghai 18
Bob male Beijing 30
Mary female Guangzhou 25
James male Shenzhen 40

二、按实际数据值排序

1
user_info.sort_values(by='age')
age city gender
username
Tom 18 Shanghai male
Mary 25 Guangzhou female
Bob 30 Beijing male
James 40 Shenzhen male

如果排序依据不止一个参考,则传入一个列表即可:

1
user_info.sort_values(by=['age', 'city'])
age city gender
username
Tom 18 Shanghai male
Mary 25 Guangzhou female
Bob 30 Beijing male
James 40 Shenzhen male
1
2
# 在排序的基础上获取最年轻的两个
user_info.sort_values(by='age').head(2)
age city gender
username
Tom 18 Shanghai male
Mary 25 Guangzhou female

上面获取最年轻的两个元素看起来没啥毛病。先排序,然后获取最前面的两个就是最小的元素了。

但是如果数据量大的时候,排序会造成很大的浪费,Pandas 的内置方法 nlargestnsmallest 方法可以快速获取最大、最小的 n 个值:

1
user_info.nlargest(2, columns='age')
age city gender
username
James 40 Shenzhen male
Bob 30 Beijing male
1
user_info.age.nlargest(2)
username
James    40
Bob      30
Name: age, dtype: int64

函数应用

常用的函数有 mapapplyapplymap

map

map 是 Series 中特有的方法,通过它可以对 Series 中的每个元素实现变换。

比如判断用户是否属于中年人(年龄大于30岁):

1
user_info.age.map(lambda x: 'yes' if x >= 30 else 'no')
username
Tom       no
Bob      yes
Mary      no
James    yes
Name: age, dtype: object

另外,如果我想判断是北方还是南方,可以这么操作:

1
2
3
4
5
6
7
8
city_map = {
'Shanghai': 'south',
'Beijing': 'north',
'Guangzhou': 'south',
'Shenzhen': 'south'
}

user_info.city.map(city_map)
username
Tom      south
Bob      north
Mary     south
James    south
Name: city, dtype: object

apply

apply 既支持 Series,也支持 DataFrame。对 Series 操作没啥问题,就是作用到每个值上,在对 DataFrame 操作的时候会根据 axis 参数作用到所有列或者所有行。

1
user_info
age city gender
username
Tom 18 Shanghai male
Bob 30 Beijing male
Mary 25 Guangzhou female
James 40 Shenzhen male
1
user_info.age.apply(lambda x: 'yes' if x >= 30 else 'no')
username
Tom       no
Bob      yes
Mary      no
James    yes
Name: age, dtype: object
1
user_info.apply(lambda x: x.max(), axis=0)
age             40
city      Shenzhen
gender        male
dtype: object

applymap

applymap 适用于 DataFrame,类似 apply

1
user_info.applymap(lambda x: str(x).lower())
age city gender
username
Tom 18 shanghai male
Bob 30 beijing male
Mary 25 guangzhou female
James 40 shenzhen male

修改索引/列的名称

直接使用 rename 即可修改:

1
2
new_info = user_info.rename(columns={'age': 'Age', 'city': 'City'})
new_info
Age City gender
username
Tom 18 Shanghai male
Bob 30 Beijing male
Mary 25 Guangzhou female
James 40 Shenzhen male
1
2
new_info = user_info.rename(index={'Tom': 'Jack'})
new_info
age city gender
username
Jack 18 Shanghai male
Bob 30 Beijing male
Mary 25 Guangzhou female
James 40 Shenzhen male

数据类型操作

1
2
# 获取每种类型的列数
user_info.get_dtype_counts()
int64     1
object    2
dtype: int64
1
2
3
# 转换数据类型
new_info = user_info['age'].astype(float)
new_info
username
Tom      18.0
Bob      30.0
Mary     25.0
James    40.0
Name: age, dtype: float64

数据转换有时候会涉及到将 object 转换成其他类型,常见的有转为数字、日期、时间差,Pandas 中分别对应 to_numericto_datetimeto_timedelta 方法

举个例子,我们先给用户添加一些身高数据:

1
2
user_info['height'] = ['178', '168', '155', '188cm']
user_info
age city gender height
username
Tom 18 Shanghai male 178
Bob 30 Beijing male 168
Mary 25 Guangzhou female 155
James 40 Shenzhen male 188cm

现在我们想把身高这一列的数据转换为数字,但是 188cm 并不是数字,我们在转换的时候可以通过 errors 参数来处理转换失败的情况。

默认情况下 errors='raise',意味着转换失败会直接抛异常,设置 errors='coerce' 可以在转换失败的时候赋值为 pd.NaT 或者 pd.NaN,设置 errors='ignore' 则在转换失败的时候返回原有的数据。

1
pd.to_numeric(user_info.height, errors='coerce')
username
Tom      178.0
Bob      168.0
Mary     155.0
James      NaN
Name: height, dtype: float64
1
pd.to_numeric(user_info.height, errors='ignore')
username
Tom        178
Bob        168
Mary       155
James    188cm
Name: height, dtype: object
1
2
3
user_info['height'] = pd.to_numeric(user_info.height, errors='ignore')

user_info
age city gender height
username
Tom 18 Shanghai male 178
Bob 30 Beijing male 168
Mary 25 Guangzhou female 155
James 40 Shenzhen male 188cm