为了账号安全,请及时绑定邮箱和手机立即绑定

处理10亿行数据的Python挑战:天气站统计分析

这篇文章: 链接

在处理一个包含10亿行的文件时,需要进行优化,以便高效处理如此庞大的数据集。在为气象站计算例如最小值、最大值和平均值等统计量等统计信息时,Python需要进行这些优化。这里有一些关键步骤和改进措施,这些措施借鉴了Go语言的方法,并针对Python进行了相应的调整。

本文为免责声明:GPT-4帮我写了这篇文章,结合了它的建议和我的个人实验。

第一步:一个简单且未经优化的方法

最简单的做法是用Python打开文件,逐行读取,将每一行拆分成站名和温度,并将它们存储在字典中。下面是一个使用Python标准库的基本实现,如下所示:

    导入 csv 模块  
    从 collections 导入 defaultdict  

    类 WeatherStationStats:  
        def __init__(self, min_temp, max_temp, sum_temp, count):  
            self.min = min_temp  
            self.max = max_temp  
            self.sum = sum_temp  
            self.count = count  

    def solution1(file_path):  
        weather_data = defaultdict(lambda: WeatherStationStats(float('inf'), float('-inf'), 0, 0))  

        with open(file_path, newline='', encoding='utf-8') as file:  
            reader = csv.reader(file, delimiter=';')  
            for 行 in reader:  
                station, temp = 行  
                temp = float(temp)  

                station_stats = weather_data[station]  
                station_stats.min = min(station_stats.min, temp)  
                station_stats.max = max(station_stats.max, temp)  
                station_stats.sum += temp  
                station_stats.count += 1  

        result = {站名: (stats.min, stats.sum / stats.count, stats.max) for 站名, stats in weather_data.items()}  
        return result

这种方法虽然可以实现,但由于没有进行优化,对于包含10亿行的数据文件来说,性能会非常慢,处理起来会很吃力。

第二步:通过内存管理来优化

在 Go 中的一个关键优化是避免过多处理字符串,而使用直接处理字节。在 Python 中,你可以通过优化文件读取和处理的方式来达到类似的优化效果。而不是将所有数据读入内存,你可以使用 Python 的 mmap 库将文件映射到内存,从而避免将其全部加载到 RAM。

    import mmap   

    def solution2(file_path):  
        # 将每个气象站的数据初始化为WeatherStationStats对象
        weather_data = defaultdict(lambda: WeatherStationStats(float('inf'), float('-inf'), 0, 0))  

        with open(file_path, 'r+b') as f:  
            # 打开文件以读写模式打开,'r+b'表示以二进制格式读写文件
            with mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) as mm:  
                # 将内存映射文件的内容读取并解码为字符串,然后按行分割
                lines = mm.read().decode().splitlines()  
                for line in lines:  
                    # 将每一行分割成气象站名称和温度
                    station, temp = line.split(';')  
                    # 将温度转换为浮点数
                    temp = float(temp)  

                    # 获取当前气象站的统计数据
                    station_stats = weather_data[station]  
                    # 更新当前气象站的最低温度
                    station_stats.min = min(station_stats.min, temp)  
                    # 更新当前气象站的最高温度
                    station_stats.max = max(station_stats.max, temp)  
                    # 更新当前气象站的温度总和
                    station_stats.sum += temp  
                    # 更新当前气象站的温度计数
                    station_stats.count += 1  

        # 生成结果字典,包含每个气象站的最低温度、平均温度和最高温度
        result = {station: (stats.min, stats.sum / stats.count, stats.max) for station, stats in weather_data.items()}  
        # 返回结果
        return result

通过使用 _mmap,你可以减少内存消耗,因为它一次只读取文件的一部分。

步骤三:并行计算

为了更好地优化,你可以将文件拆分成部分,并使用 Python 的 multiprocessing 模块并行处理这些部分。这和 Go 语言中并行处理块的方法很相似。

    import multiprocessing  
    import csv  

    def 处理块(chunk):  
        天气数据 = defaultdict(lambda: 气象站统计数据(float('inf'), float('-inf'), 0, 0))  

        for 行 in chunk:  
            站点, 温度 = 行  
            温度 = float(温度)  

            站点统计数据 = 天气数据[站点]  
            站点统计数据.min = min(站点统计数据.min, 温度)  
            站点统计数据.max = max(站点统计数据.max, 温度)  
            站点统计数据.sum += 温度  
            站点统计数据.count += 1  

        return 天气数据  

    def 解决方案3(文件路径):  
        进程池 = multiprocessing.Pool(processes=multiprocessing.cpu_count())  

        with open(文件路径, newline='', encoding='utf-8') as 文件:  
            读取器 = csv.reader(文件, delimiter=';')  
            块 = []  
            块大小 = 100000  # 每个块的行数  
            块列表 = []  

            for 行 in 读取器:  
                块.append(行)  
                if len(块) >= 块大小:  
                    块列表.append(块)  
                    块 = []  

            if 块:  
                块列表.append(块)  

        结果列表 = 进程池.map(处理块, 块列表)  

        # 这个函数读取CSV文件,将数据分割成块,使用多进程处理每一块,然后汇总所有块的数据以计算每个气象站的最低温度、平均温度和最高温度.
        # 合并所有块的结果  
        最终天气数据 = defaultdict(lambda: 气象站统计数据(float('inf'), float('-inf'), 0, 0))  
        for 天气数据 in 结果列表:  
            for 站点, 数据 in 天气数据.items():  
                最终统计数据 = 最终天气数据[站点]  
                最终统计数据.min = min(最终统计数据.min, 数据.min)  
                最终统计数据.max = max(最终统计数据.max, 数据.max)  
                最终统计数据.sum += 数据.sum  
                最终统计数据.count += 数据.count  

        结果 = {站点: (数据.min, 数据.sum / 数据.count, 数据.max) for 站点, 数据 in 最终天气数据.items()}  # 结果 = {站名: (最小温度, 平均温度, 最大温度) for 站名, 数据 in 最终天气数据.items()}
        return 结果

这种做法将文件分成易于处理的块,进行并行处理,最后再合并结果。

第4步:高级优化技巧:
1. 整数表示的温度

相比之下,你可以将温度乘以10来使用整数,从而简化与浮点运算相关的计算成本。

    temp = int(float(temp) * 10)  # 将温度值转换为整数,先转换为浮点数再乘以10
2. 自定义的数据结构:

你可以用更高效的数据结构来替代 Python 字典,或者甚至使用像 NumPy 或 Pandas 这样的库来更快地处理大规模数据。例如,比如使用 Pandas:

导入 pandas 库  
# 处理CSV文件

def solution_pandas(file_path):  
    df = pd.read_csv(file_path, delimiter=';', names=['station', 'temp'])  
    # 按站点分组并计算温度的最小值、平均值和最大值
    stats = df.groupby('station')['temp'].agg(['min', 'mean', 'max'])  
    # 返回分组统计数据的字典形式
    return stats.to_dict('index')

Pandas在处理大规模数据集时表现优越,能够比纯Python代码更快地完成这些操作,特别是在利用并行处理时。

结论

通过在Python中实施以下优化措施:

  1. 高效地处理文件 使用 mmap 或类似方法,
  2. 并行处理 使用 multiprocessing
  3. 优化的字符串处理 和尽量减少浮点运算,
  4. 高级库 比如 Pandas,

你可以显著减少处理10亿行数据所需的时间,从数小时缩短至几分钟,甚至秒级,这取决于你的数据集复杂性和基础设施。

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消