import pandas as pd
import numpy as np
import vega_datasets as vd
import plotly.express as px
18 变量的条件性转换
18.1 介绍
在上一课中,你学习了 pandas 中数据转换的基础知识。
在本课中,我们将探讨如何使用 replace()
和自定义函数等方法在 pandas 中条件性地转换变量。
条件性转换在你需要根据特定条件重新编码变量或创建新变量时非常重要。
让我们开始吧!
18.2 学习目标
完成本课后,你将能够:
- 使用
replace()
和字典根据条件转换或创建新变量。 - 知道如何在
replace()
转换中处理NaN
值。 - 能够定义和应用自定义函数来重新编码变量。
18.3 包
本课将需要 pandas
、numpy
、plotly.express
和 vega_datasets
:
18.4 replace()
简介
数据整理中的一个常见任务是根据某些条件替换列中的值。pandas 中的 replace()
方法是一个多功能的工具。
在 tips
数据集中,day
列包含简写的星期名称:
= px.data.tips()
tips 'day'].unique() tips[
array(['Sun', 'Sat', 'Thur', 'Fri'], dtype=object)
我们的目标是用完整的星期名称替换这些缩写。
我们可以创建一个将缩写名称映射到完整名称的字典:
= {
day_mapping "Sun": "Sunday",
"Sat": "Saturday",
"Fri": "Friday",
"Thur": "Thursday"
}
现在,我们使用带有字典的 replace()
方法:
'day_full'] = tips['day'].replace(day_mapping)
tips[ tips
total_bill | tip | sex | smoker | day | time | size | day_full | |
---|---|---|---|---|---|---|---|---|
0 | 16.99 | 1.01 | Female | No | Sun | Dinner | 2 | Sunday |
1 | 10.34 | 1.66 | Male | No | Sun | Dinner | 3 | Sunday |
2 | 21.01 | 3.50 | Male | No | Sun | Dinner | 3 | Sunday |
... | ... | ... | ... | ... | ... | ... | ... | ... |
241 | 22.67 | 2.00 | Male | Yes | Sat | Dinner | 2 | Saturday |
242 | 17.82 | 1.75 | Male | No | Sat | Dinner | 2 | Saturday |
243 | 18.78 | 3.00 | Female | No | Thur | Dinner | 2 | Thursday |
244 rows × 8 columns
或者,我们可以在 replace()
方法中直接进行替换,而无需明确地定义字典:
'day_full'] = tips['day'].replace({
tips["Sun": "Sunday",
"Sat": "Saturday",
"Fri": "Friday",
"Thur": "Thursday"
})'day', 'day_full']].head() tips[[
day | day_full | |
---|---|---|
0 | Sun | Sunday |
1 | Sun | Sunday |
2 | Sun | Sunday |
3 | Sun | Sunday |
4 | Sun | Sunday |
18.5 练习题:缩写性别
使用 tips
数据集,将 sex
列中的值缩写性别:
- 将
"Female"
替换为"F"
。 - 将
"Male"
替换为"M"
。
将结果赋值给一个名为 sex_abbr
的新列并显示前几行。
# Your code here:
18.6 使用 replace()
处理缺失值
有时,你的数据集中可能包含缺失值(NaN
或 None
),你希望用占位符或特定值替换它们。replace()
方法可以处理这种情况。
让我们检查 vega_datasets 中 movies
数据集的 Creative_Type
列:
= vd.data.movies()
movies 'Creative_Type'].value_counts(dropna=False) movies[
Creative_Type
Contemporary Fiction 1453
None 446
Historical Fiction 350
...
Factual 49
Super Hero 49
Multiple Creative Types 1
Name: count, Length: 10, dtype: int64
注意 Creative_Type
列中有一些 None
值。
让我们将 None
替换为 "Unknown/Unclear"
:
'Creative_Type'] = movies['Creative_Type'].replace({
movies[None: "Unknown/Unclear", # 👈 在这一行,None 是键
})
现在,让我们验证替换:
'Creative_Type'].value_counts(dropna=False) movies[
Creative_Type
Contemporary Fiction 1453
Unknown/Unclear 446
Historical Fiction 350
...
Factual 49
Super Hero 49
Multiple Creative Types 1
Name: count, Length: 10, dtype: int64
虽然 None
通常用于表示缺失的字符串,NaN
用于表示缺失的数字。考虑 US_DVD_Sales
列:
"US_DVD_Sales.isna()").shape # 检查缺失值的数量 movies.query(
(2637, 16)
'US_DVD_Sales'].tail(10) # 查看最后 10 个值。有些缺失。 movies[
3191 3273039.0
3192 22025352.0
3193 NaN
...
3198 6679409.0
3199 NaN
3200 NaN
Name: US_DVD_Sales, Length: 10, dtype: float64
我们可以使用 replace()
将 NaN
替换为 0:
'US_DVD_Sales'] = movies['US_DVD_Sales'].replace({
movies[0 # 👈 pandas 中用 `np.nan` 表示 `NaN`
np.nan: })
让我们验证替换:
'US_DVD_Sales'].tail(10) movies[
3191 3273039.0
3192 22025352.0
3193 0.0
...
3198 6679409.0
3199 0.0
3200 0.0
Name: US_DVD_Sales, Length: 10, dtype: float64
"US_DVD_Sales.isna()").shape movies.query(
(0, 16)
18.7 练习题:标准化 MPAA 分级
在 movies
数据集中,MPAA_Rating
列包含电影分级。有些条目是 None
或 "Not Rated"
。将 None
和 "Not Rated"
都替换为 "Unrated"
。
然后,使用 value_counts()
查看有多少电影未分级。该类别下应该有 699 部电影。
# Your code here:
18.8 使用自定义函数对数值数据分类
回想我们上一课的内容,我们可以使用带有条件逻辑的自定义函数来转换变量。例如,我们可以根据以下标准将 US_Gross
列分类为三类:
- 如果值小于 1000 万,类别为
"Low"
。 - 如果值在 1000 万到 5000 万之间,类别为
"Medium"
。 - 如果值大于 5000 万,类别为
"High"
。
def categ_gross(gross):
if gross < 10000000:
return "Low"
elif gross >= 10000000 and gross <= 50000000:
return "Medium"
elif gross > 50000000:
return "High"
else:
return None
= np.vectorize(categ_gross) categ_gross_vec
在上述情况下,np.vectorize
函数将返回 None
作为字符串。为了强制使用 None
类型,你可以使用 otypes
参数:
= np.vectorize(categ_gross, otypes=[object]) categ_gross_vec
现在我们可以将其应用于整个列:
'Gross_Category'] = categ_gross_vec(movies['US_Gross'])
movies['Gross_Category'].value_counts(dropna=False) movies[
Gross_Category
Medium 1241
Low 1046
High 907
None 7
Name: count, dtype: int64
这也可以通过 pd.cut()
、np.where()
和 np.select()
实现。但自定义函数方法是最灵活的。下面我们将看到如何将其扩展到更复杂的条件。
18.9 使用自定义函数进行复杂转换
自定义函数的灵活性可以轻松扩展到更复杂的条件转换。
例如,假设我们想根据美国和全球的总收入将超级英雄电影标记为“美国动作电影”或“全球动作电影”。
- 对于超级英雄电影,如果美国总收入和全球总收入相同(表明销售仅在美国),则电影被标记为美国动作电影。
- 对于超级英雄电影,如果全球总收入大于美国总收入,电影被标记为全球动作电影。
- 对于所有其他电影,保留空白标记。
我们可以定义一个接受三个参数并返回适当标记的函数:
# 定义根据条件标记电影的函数
def flag_movie(movie_type, us, worldwide):
if movie_type == 'Super Hero' and us == worldwide:
return 'US action movie'
elif movie_type == 'Super Hero' and worldwide > us:
return 'Global action movie'
else:
return None
让我们用几个值集来测试它:
print(flag_movie(movie_type='Super Hero', us=100, worldwide=100))
print(flag_movie(movie_type='Super Hero', us=100, worldwide=200))
print(flag_movie(movie_type='Comedy', us=100, worldwide=100))
US action movie
Global action movie
None
现在,让我们将其向量化:
= np.vectorize(flag_movie) flag_movie_vec
我们现在可以将其应用到列:
'Action_Flag'] = flag_movie_vec(movies['Creative_Type'], movies['US_Gross'], movies['Worldwide_Gross'])
movies[ movies
Title | US_Gross | Worldwide_Gross | US_DVD_Sales | Production_Budget | Release_Date | MPAA_Rating | Running_Time_min | Distributor | Source | Major_Genre | Creative_Type | Director | Rotten_Tomatoes_Rating | IMDB_Rating | IMDB_Votes | Gross_Category | Action_Flag | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | The Land Girls | 146083.0 | 146083.0 | 0.0 | 8000000.0 | Jun 12 1998 | R | NaN | Gramercy | None | None | Unknown/Unclear | None | NaN | 6.1 | 1071.0 | Low | None |
1 | First Love, Last Rites | 10876.0 | 10876.0 | 0.0 | 300000.0 | Aug 07 1998 | R | NaN | Strand | None | Drama | Unknown/Unclear | None | NaN | 6.9 | 207.0 | Low | None |
2 | I Married a Strange Person | 203134.0 | 203134.0 | 0.0 | 250000.0 | Aug 28 1998 | None | NaN | Lionsgate | None | Comedy | Unknown/Unclear | None | NaN | 6.8 | 865.0 | Low | None |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
3198 | Zoom | 11989328.0 | 12506188.0 | 6679409.0 | 35000000.0 | Aug 11 2006 | PG | NaN | Sony Pictures | Based on Comic/Graphic Novel | Adventure | Super Hero | Peter Hewitt | 3.0 | 3.4 | 7424.0 | Medium | Global action movie |
3199 | The Legend of Zorro | 45575336.0 | 141475336.0 | 0.0 | 80000000.0 | Oct 28 2005 | PG | 129.0 | Sony Pictures | Remake | Adventure | Historical Fiction | Martin Campbell | 26.0 | 5.7 | 21161.0 | Medium | None |
3200 | The Mask of Zorro | 93828745.0 | 233700000.0 | 0.0 | 65000000.0 | Jul 17 1998 | PG-13 | 136.0 | Sony Pictures | Remake | Adventure | Historical Fiction | Martin Campbell | 82.0 | 6.7 | 4789.0 | High | None |
3201 rows × 18 columns
要查看基于我们标记的电影类别分布,我们可以使用 value_counts()
:
'Action_Flag'].value_counts(dropna=False) movies[
Action_Flag
None 3152
Global action movie 42
US action movie 7
Name: count, dtype: int64
18.9.1 练习:根据评分标记电影
在 movies
数据集中,根据烂番茄(Rotten Tomatoes)和 IMDB 评分将电影标记为影评人友好或商业化。
- 如果烂番茄评分高于 70% 且 IMDB 评分低于 5,电影被标记为影评人友好。
- 如果烂番茄评分低于 50% 且 IMDB 评分高于 7,电影被标记为商业化。
- 否则,电影分类为其他。
- 统计有多少电影是影评人友好和商业化。应该有 13 部影评人友好电影和 33 部商业化电影。你认识其中的任何电影吗?
# Your code here:
18.10 总结
在本课中,你学习了如何使用以下方法在 pandas 中有条件地转换变量:
- 使用带有字典的
replace()
方法来映射和替换特定值。 - 在替换过程中处理缺失值(
NaN
或None
)。 - 定义自定义函数并应用它们来处理复杂条件。
这些技术是数据清洗和预处理的强大工具,使你能够重新塑造数据以满足分析需求。
下次见!