Pandas中强大的数据分组与聚合:GroupBy和Agg函数详解

Pandas中强大的数据分组与聚合:GroupBy和Agg函数详解

参考:pandas groupby agg

Pandas是Python中最流行的数据处理库之一,它提供了强大的数据操作和分析工具。在处理大型数据集时,我们经常需要对数据进行分组和聚合操作,以便更好地理解和分析数据。Pandas的GroupBy和Agg函数就是为此而生的,它们能够帮助我们轻松地对数据进行分组、聚合和统计分析。本文将深入探讨Pandas中GroupBy和Agg函数的使用方法、常见应用场景以及一些高级技巧。

1. GroupBy函数简介

GroupBy函数是Pandas中用于数据分组的核心功能。它允许我们根据一个或多个列的值将数据分成不同的组,然后对每个组进行独立的操作和分析。GroupBy的基本语法如下:

import pandas as pd

# 创建示例数据
df = pd.DataFrame({
    'name': ['Alice', 'Bob', 'Charlie', 'David', 'Eve'],
    'age': [25, 30, 35, 28, 32],
    'city': ['New York', 'London', 'Paris', 'Tokyo', 'London'],
    'salary': [50000, 60000, 70000, 55000, 65000]
})

# 按城市分组
grouped = df.groupby('city')

在这个例子中,我们创建了一个包含姓名、年龄、城市和薪资信息的DataFrame,然后使用groupby('city')按城市对数据进行分组。这个操作会返回一个GroupBy对象,我们可以在这个对象上进行进一步的操作。

2. 常见的GroupBy操作

2.1 计算组内统计量

GroupBy对象提供了许多内置的统计函数,如mean()、sum()、count()等,可以直接应用于分组后的数据。

import pandas as pd

df = pd.DataFrame({
    'name': ['Alice', 'Bob', 'Charlie', 'David', 'Eve'],
    'age': [25, 30, 35, 28, 32],
    'city': ['New York', 'London', 'Paris', 'Tokyo', 'London'],
    'salary': [50000, 60000, 70000, 55000, 65000]
})

# 计算每个城市的平均薪资
avg_salary = df.groupby('city')['salary'].mean()
print("Average salary by city:", avg_salary)

# 计算每个城市的员工数量
employee_count = df.groupby('city').size()
print("Employee count by city:", employee_count)

Output:

Pandas中强大的数据分组与聚合:GroupBy和Agg函数详解

在这个例子中,我们首先计算了每个城市的平均薪资,然后计算了每个城市的员工数量。groupby('city')['salary'].mean()会返回一个Series,其中索引是城市名,值是该城市的平均薪资。groupby('city').size()则返回每个城市的员工数量。

2.2 多列分组

GroupBy支持同时按多个列进行分组,这在处理复杂的数据结构时非常有用。

import pandas as pd

df = pd.DataFrame({
    'name': ['Alice', 'Bob', 'Charlie', 'David', 'Eve', 'Frank'],
    'age': [25, 30, 35, 28, 32, 40],
    'city': ['New York', 'London', 'Paris', 'Tokyo', 'London', 'New York'],
    'department': ['IT', 'HR', 'Finance', 'IT', 'HR', 'Finance'],
    'salary': [50000, 60000, 70000, 55000, 65000, 75000]
})

# 按城市和部门分组,计算平均薪资
avg_salary = df.groupby(['city', 'department'])['salary'].mean()
print("Average salary by city and department:", avg_salary)

Output:

Pandas中强大的数据分组与聚合:GroupBy和Agg函数详解

在这个例子中,我们按城市和部门两个维度进行分组,然后计算每个组的平均薪资。结果是一个多级索引的Series,其中第一级索引是城市,第二级索引是部门。

2.3 应用自定义函数

除了使用内置的统计函数,我们还可以使用apply()方法将自定义函数应用于每个分组。

import pandas as pd

df = pd.DataFrame({
    'name': ['Alice', 'Bob', 'Charlie', 'David', 'Eve'],
    'age': [25, 30, 35, 28, 32],
    'city': ['New York', 'London', 'Paris', 'Tokyo', 'London'],
    'salary': [50000, 60000, 70000, 55000, 65000]
})

# 定义自定义函数
def salary_range(group):
    return pd.Series({
        'min_salary': group['salary'].min(),
        'max_salary': group['salary'].max(),
        'range': group['salary'].max() - group['salary'].min()
    })

# 应用自定义函数
salary_stats = df.groupby('city').apply(salary_range)
print("Salary statistics by city:", salary_stats)

在这个例子中,我们定义了一个salary_range函数,它计算每个组的最低薪资、最高薪资和薪资范围。然后我们使用apply()方法将这个函数应用于按城市分组后的数据。结果是一个DataFrame,其中包含了每个城市的薪资统计信息。

3. Agg函数详解

Agg函数是GroupBy操作的一个强大扩展,它允许我们在一次操作中对多个列应用多个聚合函数。Agg函数的灵活性使得它成为数据分析中不可或缺的工具。

3.1 基本用法

Agg函数的基本语法如下:

import pandas as pd

df = pd.DataFrame({
    'name': ['Alice', 'Bob', 'Charlie', 'David', 'Eve'],
    'age': [25, 30, 35, 28, 32],
    'city': ['New York', 'London', 'Paris', 'Tokyo', 'London'],
    'salary': [50000, 60000, 70000, 55000, 65000]
})

# 使用agg函数计算多个统计量
stats = df.groupby('city').agg({
    'age': ['mean', 'max'],
    'salary': ['mean', 'min', 'max']
})
print("Statistics by city:", stats)

Output:

Pandas中强大的数据分组与聚合:GroupBy和Agg函数详解

在这个例子中,我们使用agg函数同时计算了每个城市的平均年龄、最大年龄、平均薪资、最低薪资和最高薪资。结果是一个多级列的DataFrame,其中第一级列是原始列名,第二级列是应用的聚合函数。

3.2 使用自定义函数

Agg函数也支持使用自定义函数进行聚合操作。

import pandas as pd
import numpy as np

df = pd.DataFrame({
    'name': ['Alice', 'Bob', 'Charlie', 'David', 'Eve'],
    'age': [25, 30, 35, 28, 32],
    'city': ['New York', 'London', 'Paris', 'Tokyo', 'London'],
    'salary': [50000, 60000, 70000, 55000, 65000]
})

# 定义自定义函数
def range_func(x):
    return x.max() - x.min()

# 使用自定义函数和内置函数
stats = df.groupby('city').agg({
    'age': ['mean', range_func],
    'salary': ['mean', 'std', range_func]
})
print("Advanced statistics by city:", stats)

Output:

Pandas中强大的数据分组与聚合:GroupBy和Agg函数详解

在这个例子中,我们定义了一个range_func函数来计算数据的范围(最大值减最小值)。然后我们在agg函数中同时使用了这个自定义函数和内置的统计函数。

3.3 重命名聚合结果

当使用agg函数时,结果的列名可能会变得复杂和难以理解。我们可以通过传递一个包含自定义名称的字典来重命名聚合结果。

import pandas as pd

df = pd.DataFrame({
    'name': ['Alice', 'Bob', 'Charlie', 'David', 'Eve'],
    'age': [25, 30, 35, 28, 32],
    'city': ['New York', 'London', 'Paris', 'Tokyo', 'London'],
    'salary': [50000, 60000, 70000, 55000, 65000]
})

# 使用自定义名称
stats = df.groupby('city').agg({
    'age': [('avg_age', 'mean'), ('max_age', 'max')],
    'salary': [('avg_salary', 'mean'), ('min_salary', 'min'), ('max_salary', 'max')]
})
print("Renamed statistics by city:", stats)

Output:

Pandas中强大的数据分组与聚合:GroupBy和Agg函数详解

在这个例子中,我们为每个聚合操作指定了一个自定义名称。结果DataFrame的列名将使用这些自定义名称,使得结果更易读和理解。

4. 高级GroupBy和Agg技巧

4.1 过滤分组

有时我们可能只对满足某些条件的组感兴趣。Pandas提供了filter()方法来实现这一功能。

import pandas as pd

df = pd.DataFrame({
    'name': ['Alice', 'Bob', 'Charlie', 'David', 'Eve', 'Frank'],
    'age': [25, 30, 35, 28, 32, 40],
    'city': ['New York', 'London', 'Paris', 'Tokyo', 'London', 'New York'],
    'salary': [50000, 60000, 70000, 55000, 65000, 75000]
})

# 只保留平均薪资大于60000的城市
filtered_df = df.groupby('city').filter(lambda x: x['salary'].mean() > 60000)
print("Filtered DataFrame:", filtered_df)

Output:

Pandas中强大的数据分组与聚合:GroupBy和Agg函数详解

在这个例子中,我们使用filter()方法只保留了平均薪资超过60000的城市的数据。

4.2 转换分组数据

transform()方法允许我们对分组数据进行转换,并将结果广播回原始DataFrame的形状。

import pandas as pd

df = pd.DataFrame({
    'name': ['Alice', 'Bob', 'Charlie', 'David', 'Eve'],
    'age': [25, 30, 35, 28, 32],
    'city': ['New York', 'London', 'Paris', 'Tokyo', 'London'],
    'salary': [50000, 60000, 70000, 55000, 65000]
})

# 计算每个城市的平均薪资,并添加到原始DataFrame
df['city_avg_salary'] = df.groupby('city')['salary'].transform('mean')
print("DataFrame with city average salary:", df)

Output:

Pandas中强大的数据分组与聚合:GroupBy和Agg函数详解

在这个例子中,我们使用transform()方法计算了每个城市的平均薪资,并将结果添加为原始DataFrame的一个新列。

4.3 动态聚合

有时我们可能需要根据数据的特征动态选择聚合函数。我们可以使用字典推导式来实现这一点。

import pandas as pd

df = pd.DataFrame({
    'name': ['Alice', 'Bob', 'Charlie', 'David', 'Eve'],
    'age': [25, 30, 35, 28, 32],
    'city': ['New York', 'London', 'Paris', 'Tokyo', 'London'],
    'salary': [50000, 60000, 70000, 55000, 65000],
    'department': ['IT', 'HR', 'Finance', 'IT', 'HR']
})

# 动态选择聚合函数
agg_funcs = {
    'age': 'mean',
    'salary': ['mean', 'max'] if df['salary'].mean() > 60000 else 'mean'
}

result = df.groupby('city').agg(agg_funcs)
print("Dynamic aggregation result:", result)

Output:

Pandas中强大的数据分组与聚合:GroupBy和Agg函数详解

在这个例子中,我们根据整个DataFrame的平均薪资来决定是否对薪资列应用多个聚合函数。

4.4 分组排序

我们可以在分组后对每个组内的数据进行排序。

import pandas as pd

df = pd.DataFrame({
    'name': ['Alice', 'Bob', 'Charlie', 'David', 'Eve', 'Frank'],
    'age': [25, 30, 35, 28, 32, 40],
    'city': ['New York', 'London', 'Paris', 'Tokyo', 'London', 'New York'],
    'salary': [50000, 60000, 70000, 55000, 65000, 75000]
})

# 在每个城市内按薪资降序排序
sorted_df = df.groupby('city').apply(lambda x: x.sort_values('salary', ascending=False))
print("Sorted DataFrame within groups:", sorted_df)

在这个例子中,我们使用apply()方法对每个城市内的数据按薪资降序排序。

4.5 分组窗口函数

Pandas提供了强大的窗口函数,可以在分组数据上执行滑动窗口计算。

import pandas as pd

df = pd.DataFrame({
    'date': pd.date_range(start='2023-01-01', periods=10),
    'city': ['New York', 'London', 'Paris', 'Tokyo', 'London'] * 2,
    'sales': [100, 150, 200, 120, 180, 90, 160, 210, 130, 170]
})

# 计算每个城市的3天滑动平均销售额
df['rolling_avg_sales'] = df.groupby('city')['sales'].rolling(window=3, min_periods=1).mean().reset_index(level=0, drop=True)
print("DataFrame with rolling average sales:", df)

Output:

Pandas中强大的数据分组与聚合:GroupBy和Agg函数详解

在这个例子中,我们使用rolling()函数计算了每个城市的3天滑动平均销售额。min_periods=1参数确保即使不足3天的数据也能计算平均值。

5. GroupBy和Agg的性能优化

在处理大型数据集时,GroupBy和Agg操作可能会变得很慢。以下是一些提高性能的技巧:

5.1 使用categoricals

如果分组键是字符串,将其转换为categorical类型可以显著提高性能。

import pandas as pd
import numpy as np

# 创建一个大型DataFrame
df = pd.DataFrame({
    'group': np.random.choice(['A', 'B', 'C', 'D'], size=1000000),
    'value': np.random.randn(1000000)
})

# 将group列转换为categorical
df['group'] = df['group'].astype('category')

# 执行GroupBy操作
result = df.groupby('group')['value'].mean()
print("Result with categorical group:", result)

在这个例子中,我们将’group’列转换为categorical类型,这可以加速后续的GroupBy操作。

5.2 使用numba加速自定义聚合函数

对于复杂的自定义聚合函数,可以使用numba来加速计算。

import pandas as pd
import numpy as np
from numba import jit

@jit(nopython=True)
def custom_agg(values):
    return np.mean(values) * np.median(values)

df = pd.DataFrame({
    'group': np.random.choice(['A', 'B', 'C', 'D'], size=1000000),
    'value': np.random.randn(1000000)
})

result = df.groupby('group')['value'].agg(custom_agg)
print("Result with numba-accelerated custom aggregation:", result)

在这个例子中,我们使用numba的@jit装饰器来编译自定义聚合函数,这可以显著提高大规模数据的处理速度。

5.3 使用Dask进行并行处理

对于非常大的数据集,可以考虑使用Dask库来进行并行处理。

import pandas as pd
import dask.dataframe as dd

# 假设我们有一个大型CSV文件
df = dd.read_csv('large_file.csv')

# 执行GroupBy和Agg操作
result = df.groupby('group')['value'].mean().compute()
print("Result with Dask parallel processing:", result)

这个例子展示了如何使用Dask来处理大型CSV文件,并执行GroupBy和Agg操作。Dask会自动将操作分散到多个核心上并行执行。

6. 实际应用案例

让我们通过一些实际应用案例来深入理解GroupBy和Agg的强大功能。

6.1 销售数据分析

import pandas as pd
import numpy as np

# 创建销售数据
sales_data = pd.DataFrame({
    'date': pd.date_range(start='2023-01-01', end='2023-12-31', freq='D'),
    'product': np.random.choice(['A', 'B', 'C'], size=365),
    'region': np.random.choice(['North', 'South', 'East', 'West'], size=365),
    'sales': np.random.randint(100, 1000, size=365)
})

# 按月份和产品分组,计算销售总额和平均销售额
monthly_sales = sales_data.groupby([sales_data['date'].dt.to_period('M'), 'product']).agg({
    'sales': ['sum', 'mean']
})

print("Monthly sales by product:", monthly_sales)

# 计算每个地区的销售占比
total_sales = sales_data['sales'].sum()
sales_proportion = sales_data.groupby('region')['sales'].sum() / total_sales * 100

print("Sales proportion by region:", sales_proportion)

Output:

Pandas中强大的数据分组与聚合:GroupBy和Agg函数详解

这个例子展示了如何分析销售数据,包括计算月度销售统计和各地区的销售占比。

6.2 客户行为分析

import pandas as pd
import numpy as np

# 创建客户行为数据
customer_data = pd.DataFrame({
    'customer_id': np.repeat(range(1, 101), 10),
    'purchase_date': pd.date_range(start='2023-01-01', periods=1000),
    'product_category': np.random.choice(['Electronics', 'Clothing', 'Books', 'Home'], size=1000),
    'purchase_amount': np.random.randint(10, 500, size=1000)
})

# 计算每个客户的总购买金额和平均购买金额
customer_stats = customer_data.groupby('customer_id').agg({
    'purchase_amount': ['sum', 'mean', 'count']
})
customer_stats.columns = ['total_amount', 'avg_amount', 'purchase_count']

print("Customer purchase statistics:", customer_stats.head())

# 找出购买次数最多的前10个客户
top_customers = customer_stats.nlargest(10, 'purchase_count')
print("Top 10 customers by purchase count:", top_customers)

# 分析每个产品类别的销售情况
category_sales = customer_data.groupby('product_category').agg({
    'purchase_amount': ['sum', 'mean', 'count']
})
category_sales.columns = ['total_sales', 'avg_sale', 'sale_count']
print("Sales statistics by product category:", category_sales)

Output:

Pandas中强大的数据分组与聚合:GroupBy和Agg函数详解

这个例子展示了如何分析客户购买行为,包括计算客户统计数据、识别最活跃的客户以及分析产品类别的销售情况。

7. 常见问题和解决方案

在使用GroupBy和Agg函数时,可能会遇到一些常见问题。以下是一些问题及其解决方案:

7.1 处理缺失值

import pandas as pd
import numpy as np

df = pd.DataFrame({
    'group': ['A', 'A', 'B', 'B', 'C'],
    'value': [1, np.nan, 3, 4, 5]
})

# 默认情况下,聚合函数会忽略NaN值
result1 = df.groupby('group')['value'].mean()
print("Result ignoring NaN:", result1)

# 使用skipna=False来包含NaN值
result2 = df.groupby('group')['value'].mean(skipna=False)
print("Result including NaN:", result2)

这个例子展示了如何处理分组数据中的缺失值。默认情况下,大多数聚合函数会忽略NaN值,但我们可以通过设置skipna=False来改变这一行为。

7.2 处理多级索引

import pandas as pd

df = pd.DataFrame({
    'date': pd.date_range(start='2023-01-01', periods=10),
    'city': ['New York', 'London'] * 5,
    'product': ['A', 'B', 'A', 'B', 'C'] * 2,
    'sales': [100, 150, 200, 120, 180, 90, 160, 210, 130, 170]
})

# 按日期和城市分组
grouped = df.groupby(['date', 'city'])

# 计算每组的销售总额
sales_sum = grouped['sales'].sum()
print("Sales sum with multi-level index:", sales_sum)

# 重置索引以便于进一步处理
sales_sum_reset = sales_sum.reset_index()
print("Sales sum with reset index:", sales_sum_reset)

Output:

Pandas中强大的数据分组与聚合:GroupBy和Agg函数详解

这个例子展示了如何处理GroupBy操作后产生的多级索引。我们可以使用reset_index()方法将多级索引转换为普通列。

7.3 处理大型数据集的内存问题

当处理非常大的数据集时,可能会遇到内存不足的问题。一种解决方案是使用chunksize参数分批处理数据。

import pandas as pd

# 假设我们有一个大型CSV文件
filename = 'large_file.csv'
chunk_size = 100000  # 每次读取的行数

# 初始化结果DataFrame
result = pd.DataFrame()

# 分批读取和处理数据
for chunk in pd.read_csv(filename, chunksize=chunk_size):
    # 对每个chunk进行GroupBy和Agg操作
    chunk_result = chunk.groupby('group')['value'].agg(['mean', 'sum'])
    result = result.add(chunk_result, fill_value=0)

# 计算最终结果
result['mean'] = result['sum'] / result['count']

print("Result from processing large file in chunks:", result)

这个例子展示了如何使用chunksize参数分批读取和处理大型CSV文件,从而避免内存溢出问题。

8. 总结

Pandas的GroupBy和Agg函数是数据分析中不可或缺的工具。它们提供了强大而灵活的方法来对数据进行分组、聚合和统计分析。通过本文的详细介绍和丰富的示例,我们深入探讨了这些函数的基本用法、高级技巧以及实际应用案例。

关键要点包括:
1. GroupBy函数允许我们根据一个或多个列的值将数据分成不同的组。
2. Agg函数扩展了GroupBy的功能,允许我们在一次操作中对多个列应用多个聚合函数。
3. 我们可以使用自定义函数进行更复杂的聚合操作。
4. 性能优化技巧,如使用categoricals和numba,可以显著提高大规模数据处理的效率。
5. 实际应用中,GroupBy和Agg函数可以用于各种数据分析任务,如销售数据分析和客户行为分析。

掌握这些技能将使你能够更有效地处理和分析复杂的数据集,从而做出更好的数据驱动决策。随着数据规模和复杂性的不断增加,熟练运用这些工具将成为数据分析师和数据科学家的重要技能。

Python教程

Java教程

Web教程

数据库教程

图形图像教程

大数据教程

开发工具教程

计算机教程