【附代码】Pandas的groupby加速(sort+numpy)


作者:小猪快跑

基础数学&计算数学,从事优化领域5年+,主要研究方向:MIP求解器、整数规划、随机规划、智能优化算法

如有错误,欢迎指正。如有更好的算法,也欢迎交流!!!——@小猪快跑

相关文献

测试电脑配置

博主三千元电脑的渣渣配置:

CPU model: AMD Ryzen 7 7840HS w/ Radeon 780M Graphics, instruction set [SSE2|AVX|AVX2|AVX512]
Thread count: 8 physical cores, 16 logical processors, using up to 16 threads

Pandas的groupby在重复率不同数据中的表现

在大数据处理中,我们有时候会遇到需要统计比如说每辆车今年违规了多少,但违规是个很低频的数据,也就是说一辆车发生违规的数据条数很少,这时候Pandas的groupby就不太适合了(除非能使用sum这种自带的groupby函数)。

我们具体来看测试结果。

重复数量<=10:

在这里插入图片描述

重复数量<=100:

在这里插入图片描述

重复数量<=1000:

在这里插入图片描述

重复数量<=10000:

在这里插入图片描述

代码

from collections import defaultdict
import perfplot
from matplotlib import pyplot as plt
import numpy as np
import pandas as pd


def gen_data(m: int, n: int):
    tmp = np.zeros(n, dtype=np.int64)
    tmp[:n // m * m] = list(range(n // m)) * m
    return tmp


def get_input(n, repeat_num):
    df = pd.DataFrame()
    df['a'] = gen_data(repeat_num, n)
    df['b'] = 2
    return df


def pandas_iter_rows(df):
    res = defaultdict(int)
    for i, row in df.iterrows():
        res[row['a']] += row['b']
    return [[k, v] for k, v in res.items()]


def sort_group_by(df, by, col):
    k = df[by].apply(tuple, 1).values if isinstance(by, list) else df[by].values
    v = df[col].values
    idx = k.argsort()
    k_sorted = k[idx]
    v_sorted = v[idx]

    idx2 = [-1] + np.where(k_sorted[1:] != k_sorted[:-1])[0].tolist() + [len(k_sorted) - 1]
    for i, j in zip(idx2[:-1], idx2[1:]):
        yield k_sorted[i + 1], v_sorted[i + 1:j + 1]


def hash_group_by(df, by, col):
    k = df[by].apply(tuple, 1).values if isinstance(by, list) else df[by].values
    v = df[col].values.tolist()

    res = defaultdict(list)
    for i, j in zip(k, v):
        res[i].append(j)
    for i, j in res.items():
        yield i, j


def numpy_for(df):
    res = defaultdict(int)
    for a, b in zip(df['a'], df['b']):
        res[a] += b
    return [[k, v] for k, v in res.items()]


def pandas_group_by_sum(df):
    return df.groupby('a').sum()['b'].reset_index().values.tolist()


def pandas_group_by_for_sum(df):
    return [[k, v['b'].sum()] for k, v in df.groupby('a')]


def sort_group_by_sum(df):
    return [[k, sum(v)] for k, v in sort_group_by(df, 'a', 'b')]


def hash_group_by_sum(df):
    return [[k, sum(v)] for k, v in hash_group_by(df, 'a', 'b')]


if __name__ == '__main__':
    for i in range(4):
        repeat_num = 10 ** (i + 1)
        b = perfplot.bench(
            setup=lambda n: get_input(n, repeat_num=repeat_num),
            kernels=[
                pandas_group_by_sum,
                pandas_group_by_for_sum,
                sort_group_by_sum,
                pandas_iter_rows,
                numpy_for,
                hash_group_by_sum,
            ],
            n_range=[2 ** k for k in range(20)],
            xlabel="length of DataFrame",
        )

        plt.figure(dpi=300)
        b.save(f"{repeat_num}.png")
        b.show()

总结

尽量不要用df.iterrows(),在数据分类较多的时候,又不能直接使用agg或者自带聚合函数时,pandas groupby效率偏低,更推荐使用

def sort_group_by(df, by, col):
    k = df[by].apply(tuple, 1).values if isinstance(by, list) else df[by].values
    v = df[col].values
    idx = k.argsort()
    k_sorted = k[idx]
    v_sorted = v[idx]

    idx2 = [-1] + np.where(k_sorted[1:] != k_sorted[:-1])[0].tolist() + [len(k_sorted) - 1]
    for i, j in zip(idx2[:-1], idx2[1:]):
        yield k_sorted[i + 1], v_sorted[i + 1:j + 1]
  • 9
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是一个简单示例,使用 pandasnumpy 爬取网易云新歌榜并进行数据分析: 1. 导入需要的库 ```python import requests from bs4 import BeautifulSoup import pandas as pd import numpy as np ``` 2. 获取网易云音乐新歌榜页面的 HTML 内容 ```python url = 'https://music.163.com/discover/toplist?id=3779629' headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3'} response = requests.get(url, headers=headers) html_content = response.text ``` 3. 使用 BeautifulSoup 解析 HTML 内容,并提取需要的数据 ```python soup = BeautifulSoup(html_content, 'html.parser') song_list = soup.find('ul', {'class': 'f-hide'}).find_all('a') songs = [] for song in song_list: songs.append(song.text) ``` 4. 使用 pandas 创建 DataFrame,并对数据进行处理和分析 ```python df = pd.DataFrame({'song': songs}) df['count'] = 1 df['artist'] = df['song'].apply(lambda x: x.split('-')[0]) df['name'] = df['song'].apply(lambda x: x.split('-')[1]) df.drop('song', axis=1, inplace=True) df = df[['name', 'artist', 'count']] df_grouped = df.groupby('artist')['count'].sum().reset_index() df_grouped.sort_values('count', ascending=False, inplace=True) print(df_grouped.head(10)) ``` 5. 输出结果 ``` artist count 0 10CM 1 1 ASH 1 2 Ariana Grande 1 3 BAEKHYUN 1 4 BTS 1 5 Billie Eilish 1 6 CIX 2 7 Chris Lee 1 8 D.O. 1 9 Dua Lipa 1 ``` 这个简单示例爬取了网易云音乐新歌榜的歌曲信息,以艺术家为单位进行了分组统计,并输出了前 10 名艺术家的歌曲数量。你可以根据需要进行修改和扩展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值