学Python数据科学,玩游戏、学日语、搞编程一条龙。
整套学习自学教程中应用的数据都是《三國志》、《真·三國無雙》系列游戏中的内容。
分解和可视化的方式 复习和巩固 Pandas GroupBy。无论是刚开始使用 Pandas 并想掌握其核心功能,还是希望填补对 .groupby() 的理解都是对未来工作有帮助的。
文章目录
数据准备
示例1:三国志人物数据
目标通过剖析国会历史成员的数据。
import pandas as pd
df = pd.read_excel("Romance of the Three Kingdoms 13/人物详情数据.xlsx",)
df.head()
GroupBy 聚合操作
问题1(单列聚合):想看看所有人物中的分类情况,即文臣、武将。应该如何操作?
SQL 操作。
SELECT `分類`, count(`名前`) as `数量`
FROM df
GROUP BY `分類`
ORDER BY `分類`;
Pandas 操作。
n_by_state = df.groupby("分類")["名前"].count().nlargest(2)
n_by_state
分類
武官 520
文官 336
Name: 名前, dtype: int64
问题2(多列聚合):如何按照性别和分类进行人物的区分?
SQL 操作。
SELECT `分類`,`性別` count(`名前`) as `数量`
FROM df
GROUP BY `分類`,`性別`
ORDER BY `分類`,`性別`;
Pandas 操作。
n_by_state = df.groupby(["分類", "性別"])["名前"].count()
n_by_state
分類 性別
文官 女 35
男 301
武官 女 11
男 509
? 男 1
Name: 名前, dtype: int64
GroupBy 的工作原理
.groupby() 其实是split-apply-combine 这么三步的过程:将表拆分为组、对每个较小的表应用一些操作、合并结果。
split 拆分过程
Pandas GroupBy 对象并查看拆分的一种有用方法是对其进行迭代。
by_state = df.groupby("分類")
# 查看每次聚合的元素对应的前两条数据
for state, frame in by_state:
print(f"前 2 条数据 {
state!r}")
print("------------------------")
print(frame.head(2), end="\n\n")
.groups 属性将提供一 { ‘group name’ : ‘group label’ } 的字典,by_state 是一个dict 类型的数据,因此可以实用 key 选择的方式进行访问。
list(by_state["寿命"])
[('文官', 1 70
2 65
3 67
4 60
5 57
...
845 107
848 102
850 104
852 100
855 99
Name: 寿命, Length: 336, dtype: int64), ('武官', 0 36
6 63
7 55
8 72
11 56
...
847 103
851 107
853 99
854 99
856 99
Name: 寿命, Length: 520, dtype: int64), ('?', 849 104
Name: 寿命, dtype: int64)]
可以实用 .get_group() 获取相关的详细 value 信息。
by_state.get_group("文官")
apply 应用过程
将相同的操作(或可调用)应用于拆分阶段生成的每个 单元。
state, frame = next(iter(by_state))
state
'文官'
frame.head(5)
combine 组合过程
frame["名前"].count()
336
示例2:空气质量数据集
import pandas as pd
df = pd.read_excel("数据科学必备Pandas实用操作GroupBy数据分组详解/AirQualityUCI.xlsx",parse_dates=[["Date","Time"]])
df.rename(columns={
"CO(GT)": "co",
"Date_Time": "tstamp",
"T": "temp_c",
"RH": "rel_hum",
"AH": "abs_hum",
},inplace = True
)
df.set_index("tstamp",inplace=True)
co 是该小时的平均一氧化碳读数,而 temp_c、rel_hum 和 abs_hum 分别是该小时的平均温度、相对湿度和绝对湿度。 观察从 2004 年 3 月持续到 2005 年 4 月。
df.index.min()
Timestamp('2004-03-10 18:00:00')
df.index.max()
Timestamp('2005-04-04 14:00:00')
派生数组进行分组
利用星期的数据(转化后的字符串)进行分组聚合。
day_names = df.index.day_name()
day_names[:10]
Index(['Wednesday', 'Wednesday', 'Wednesday', 'Wednesday', 'Wednesday',
'Wednesday', 'Thursday', 'Thursday', 'Thursday', 'Thursday'],
dtype='object', name='tstamp')
问题1:查找一周中某天的平均一氧化碳 ( co ) 的数据。
df.groupby(day_names)["co"].mean()
tstamp
Friday -24.583259
Monday -30.063820
Saturday -27.126414
Sunday -35.432292
Thursday -35.806176
Tuesday -41.773864
Wednesday -44.917647
Name: co, dtype: float64
问题2:按照星期每个时间段进行数据聚合。
hr = df.index.hour
df.groupby([day_names, hr])["co"].mean().rename_axis(["dow", "hr"])
dow hr
Friday 0 -30.517857
1 -30.792857
2 -31.158929
3 -31.398214
4 -92.416071
...
Wednesday 19 -28.662500
20 -28.916071
21 -29.710714
22 -30.378571
23 -30.516071
Name: co, Length: 168, dtype: float64
问题3:将温度划分离散区间进行分组聚合。
bins = pd.cut(df["temp_c"], bins=3, labels=("cool", "warm", "hot"))
df[["rel_hum", "abs_hum"]].groupby(bins).agg(["mean", "median"])
问题4:按照年度、季度聚合数据处理。
df.groupby([df.index.year, df.index.quarter])["co"].agg(["max", "min"]).rename_axis(["year", "quarter"])
示例3:新闻聚合器数据集
import datetime as dt
import pandas as pd
def parse_millisecond_timestamp(ts):
"""转换 UTC 日期时间 """
return dt.datetime.fromtimestamp(ts / 1000, tz=dt.timezone.utc)
df = pd.read_csv(
"数据科学必备Pandas实用操作GroupBy数据分组详解/newsCorpora.csv",
sep="\t",
header=None,
index_col=0,
names=["title", "url", "outlet", "category", "cluster", "host", "tstamp"],
parse_dates=["tstamp"],
date_parser=parse_millisecond_timestamp,
dtype={
"outlet": "category",
"category": "category",
"cluster": "category",
"host": "category",
},
)
df.head()
category 的类别分别是 b商业、t科技、e娱乐、m健康。
问题1:计算包含某关键字进行数据的类别的聚合统计,并进行排序。
df.groupby("outlet", sort=False)["title"].apply(
lambda ser: ser.str.contains("Fed").sum()
).nlargest(10)
outlet
Reuters 161
NASDAQ 103
Businessweek 93
Investing.com 66
Wall Street Journal \(blog\) 61
MarketWatch 56
Moneynews 55
Bloomberg 53
GlobalPost 51
Economic Times 44
Name: title, dtype: int64
提高 GroupBy 性能
.groupby() .apply() 这种模式可能不是最优的。 .apply() 是将有效地对每个组执行 Python 循环。 这每当使用 .apply() 时可以利用 .groupby() 不仅接受一个或多个列名,还接受许多类似数组的结构。提取包含关键字符的数据,生成的数据是一个 Series,然后进行聚合。
mentions_fed = df["title"].str.contains("Fed")
import numpy as np
mentions_fed.groupby(
df["outlet"], sort=False
).sum().nlargest(10).astype(np.uintc)
outlet
Reuters 161
NASDAQ 103
Businessweek 93
Investing.com 66
Wall Street Journal \(blog\) 61
MarketWatch 56
Moneynews 55
Bloomberg 53
GlobalPost 51
Economic Times 44
Name: title, dtype: uint32
判断数据是否有丢失。
df["outlet"].shape == mentions_fed.shape
True
最后看一下执行的时间对比,速度提高了很多。
# Version 1: 使用 `.apply()`
df.groupby("outlet", sort=False)["title"].apply(
lambda ser: ser.str.contains("Fed").sum()
).nlargest(10)
# Version 2: 使用 vectorization
mentions_fed = df["title"].str.contains("Fed")
mentions_fed.groupby(
df["outlet"], sort=False
).sum().nlargest(10).astype(np.uintc)
Pandas GroupBy 方法汇总
聚合方法(也称为归约方法)
将许多数据点『混合』成关于这些数据点的聚合统计信息。一个例子是取 10 个数字的总和、平均值或中位数,结果只是一个数字。
其中包括.agg()、.aggregate()、.all()、.any()、.apply()、.corr()、.corrwith()、.count()、.cov()、.cumcount()、.cummax()、.cummin()、.cumprod()、.cumsum()、.describe()、.idxmax()、.idxmin()、.mad()、.max()、.mean()、.median()、.min()、.nunique()、.prod()、.sem()、.size()、.skew()、.std()、.sum()、.var()。
过滤器方法
会返回原始 DataFrame 的子集。这通常意味着使用 .filter() 根据有关该组及其子表的一些比较统计数据来删除整个组。在此定义下包含许多从每个组中排除特定行的方法也是有意义的。
其中包括:.filter()、.first()、.head()、.last()、.nth()、.tail()、.take()。
转换方法
返回一个 DataFrame 其形状和索引与原始数据相同,但值不同。使用聚合和过滤方法,生成的 DataFrame 通常会比输入 DataFrame 的大小更小。转换并非如此,它会转换单个值本身但保留原始 DataFrame 的形状。
其中包括:.bfill()、.diff()、.ffill()、.fillna()、.pct_change()、.quantile()、.rank()、.shift()、.transform()、.tshift()。
元方法
不关注调用 .groupby() 的原始对象,而是更专注于提供高级信息,例如组的数量和这些组的索引。
其中包括:.iter()、.get_group()、.groups、.indices、.ndim、.ngroup()、.ngroups、.dtypes。
绘图方法
模仿 Pandas Series 或 DataFrame 的绘图 API,通常将输出分成多个子图。
其中包括:.hist()、.ohlc()、.boxplot().plot() 。