数据科学必备Pandas实用操作GroupBy数据分组详解

分解和可视化的方式 复习和巩固 Pandas GroupBy。无论是刚开始使用 Pandas 并想掌握其核心功能,还是希望填补对 .groupby() 的理解都是对未来工作有帮助的。
在这里插入图片描述

数据准备

  1. 美国国会数据集 包含有关国会历史成员的公共信息。
  2. 空气质量数据集 包含定期气体传感器读数。
  3. 新闻数据集 其中包含数十万条新闻文章的元数据。

示例1:美国国会数据集

目标通过剖析国会历史成员的数据。

import pandas as pd

dtypes = {
    
    
    "first_name": "category",
    "gender": "category",
    "type": "category",
    "state": "category",
    "party": "category",
}
df = pd.read_csv(
    "数据科学必备Pandas实用操作GroupBy数据分组详解/legislators-historical.csv",
    dtype=dtypes,
    usecols=list(dtypes) + ["birthday", "last_name"],
    parse_dates=["birthday"]
)

数据集包含成员的名字和姓氏、出生日期、性别、类型( “rep” 众议院/"sen"参议院)、美国州和政党。可以使用 df.head() 查看数据集的前几行。

df.head()

在这里插入图片描述
GroupBy 聚合操作

问题1(单列聚合):如果想实现数据集的整个历史中,按州计算、国会议员的数量是多少?应该如何操作?

SQL 操作。

SELECT state, count(name)
FROM df
GROUP BY state
ORDER BY state;

Pandas 操作。

n_by_state = df.groupby("state")["last_name"].count().nlargest(10) 
n_by_state.head(10)

state
NY    1467
PA    1053
OH     676
IL     488
VA     433
MA     427
KY     373
CA     368
NJ     359
NC     356
Name: last_name, dtype: int64

问题2(多列聚合):如何按照州和性别划分的国会议员人数?
SQL 操作。

SELECT state, gender, count(name)
FROM df
GROUP BY state, gender
ORDER BY state, gender;

Pandas 操作。

n_by_state = df.groupby(["state", "gender"])["first_name"].count()
n_by_state.head(10)
state  gender
AK     M          16
AL     F           3
       M         203
AR     F           5
       M         112
                ...
WI     M         196
WV     F           1
       M         119
WY     F           2
       M          38
Name: last_name, Length: 104, dtype: int64

GroupBy 的工作原理

.groupby() 其实是split-apply-combine 这么三步的过程:将表拆分为组、对每个较小的表应用一些操作、合并结果

split 拆分过程

Pandas GroupBy 对象并查看拆分的一种有用方法是对其进行迭代。

by_state = df.groupby("state")

# 查看每次聚合的元素对应的前两条数据
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 选择的方式进行访问。

by_state.groups["CO"]

在这里插入图片描述
可以实用 .get_group() 获取相关的详细 value 信息。

by_state.get_group("CO")

在这里插入图片描述

apply 应用过程

将相同的操作(或可调用)应用于拆分阶段生成的每个 单元。

state, frame = next(iter(by_state))  
state
'AK'

frame.head(3)

在这里插入图片描述
combine 组合过程

frame["first_name"].count()
17

示例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 循环。 虽然 .groupby() .apply() 模式可以提供一些灵活性,但也可以阻止 Pandas 以其他方式使用其基于 Cython 的优化。

这就是说每当发现考虑使用 .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()

猜你喜欢

转载自blog.csdn.net/qq_20288327/article/details/124191331