You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何Pandas的iterrows循环用列表推导和多线程无法提速?

优化Pandas遍历大型GeoDataFrame的性能问题

我需要遍历Pandas DataFrame的每行,执行两项操作生成新的字符串字段。这些DataFrame来自多个大型文件,执行速度至关重要。

复现用的小型GeoDataFrame

import json
import geopandas as gpd
import pandas as pd
from shapely.geometry import Polygon
from geolib import geohash as pgh
import time

coords1 = [[1.3093500680264314, 44.14597523454133],
[1.309362565037715, 44.14597542723667],
[1.3093750620490838, 44.1459756199306],
[1.3093875590605368, 44.145975812623114],
[1.3094000560720744, 44.14597600531418]]

coords2 = [[1.3123256948057254, 44.14597606298388],
[1.312338191827267, 44.145976255340116],
[1.3123506888488932, 44.14597644769494],
[1.312363185870604, 44.14597664004834],
[1.3123756828923991, 44.14597683240032]]

elev1 = [177.2, 177.1, 177.0, 177.0, 176.9]

elev2 = [164.3, 164.5, 164.7, 165.0, 165.3]

points1 = gpd.GeoDataFrame({'center': coords1, 'elevation': elev1})
points2 = gpd.GeoDataFrame({'center': coords2, 'elevation': elev2})

inputs = [points1, points2]

原始的iterrows实现

def asc_to_geohash_polygons(gdf, processed_geohashes):
    
    start_time1 = time.time()
    
    elevation_data=[]
    for index, row in gdf.iterrows():
        geohash_code = pgh.encode(row['center'][1], row['center'][0], precision=9)

        if geohash_code not in processed_geohashes:
            processed_geohashes.add(geohash_code)
            bounds = pgh.bounds(geohash_code)
            sw = bounds.sw
            ne = bounds.ne
            poly = Polygon([(sw.lon, sw.lat),
                            (ne.lon, sw.lat),
                            (ne.lon, ne.lat),
                            (sw.lon, ne.lat),
                            (sw.lon, sw.lat)])
            feature = {
                "type": "Feature",
                "properties": {
                    "geohash": geohash_code,
                    "elevation": row['elevation']
                },
                "geometry": poly.__geo_interface__
            }
            features.append(feature)
            elevation_data.append([geohash_code, row['elevation']])
    
    end_time1 = time.time()
    print(f"Timer 1: {end_time1 - start_time1} seconds")
            
    return features, elevation_data

调用函数的主逻辑

def process_all_files_in_folder(gdfs, output_geojson_path):
    all_features = []
    elevation_dict = {}  # 使用字典去重
    processed_geohashes = set()  # 全局集合跟踪已处理的geohash

    all_files = [f for f in gdfs]

    for file in all_files:
        temp_data, temp_elevations = asc_to_geohash_polygons(file, processed_geohashes)
        all_features.extend(temp_data)

        for geohash, elevation in temp_elevations:
            elevation_dict[geohash] = elevation


    all_elevations = [[geohash, elevation] for geohash, elevation in elevation_dict.items()]
    print(all_elevations)
    with open(output_geojson_path, 'w') as f:
        json.dump({"type": "FeatureCollection", "features": all_features}, f, indent=4)
        

process_all_files_in_folder(inputs, 'output.geojson')

计时后发现iterrows循环是耗时瓶颈,我尝试了三种优化方案,但效果都不如原始方法:

尝试的优化方案

(1) 使用.apply()

def encode_geohash(lat_lon_pair):
    return pgh.encode(lat_lon_pair[1], lat_lon_pair[0], precision=9)
        
def get_feature(geohash, elevation):
    bounds = pgh.bounds(geohash)
    sw = bounds.sw
    ne = bounds.ne
    poly = Polygon([(sw.lon, sw.lat),
                    (ne.lon, sw.lat),
                    (ne.lon, ne.lat),
                    (sw.lon, ne.lat),
                    (sw.lon, sw.lat)])
    feature = {
        "type": "Feature",
        "properties": {
            "geohash": geohash,
            "elevation": elevation
        },
        "geometry": poly.__geo_interface__
    }
    return feature

geohash_data = gdf['center'].apply(encode_geohash)
elevation_dataframe = pd.concat([geohash_data.rename('geohash'), gdf['elevation']], axis=1)
elevation_data = elevation_dataframe.values.tolist()
features = elevation_dataframe.apply(lambda x: get_feature(x['geohash'], x['elevation']), axis=1).tolist()
processed_geohashes.update(geohash_data.tolist())

(2) 列表推导

geohash_data = gdf['center'].apply(encode_geohash)
        
gdf['geohash'] = geohash_data
        
features = [get_feature(geohash, elevation) for elevation, geohash in zip(gdf['elevation'], gdf['geohash'])]
        
elevation_data = [[geohash, elevation] for geohash, elevation in zip(gdf['geohash'], gdf['elevation'])]
        
processed_geohashes.update(geohash_data.tolist())

(3) 多线程

from concurrent.futures import ThreadPoolExecutor
from threading import Lock

features = []
elevation_data = []
hash_lock = Lock()

pool = ThreadPoolExecutor(10)

def add_to_list(row):
    geohash_code = pgh.encode(row['center'][1], row['center'][0], precision=9)
    with hash_lock:
        if geohash_code in processed_geohashes:
            return False
        processed_geohashes.add(geohash_code)
    bounds = pgh.bounds(geohash_code)
    sw = bounds.sw
    ne = bounds.ne
    poly = Polygon([(sw.lon, sw.lat),
                    (ne.lon, sw.lat),
                    (ne.lon, ne.lat),
                    (sw.lon, ne.lat),
                    (sw.lon, sw.lat)])
    feature = {
        "type": "Feature",
        "properties": {
            "geohash": geohash_code,
            "elevation": row['elevation']
        },
        "geometry": poly.__geo_interface__
    }
    features.append(feature)
    elevation_data.append([geohash_code, row['elevation']])
    return True

tasks = []
for index, row in gdf.iterrows():
    tasks.append(pool.submit(add_to_list, row))

for task in tasks:
    task.result()

我忽略了什么?我意识到向量化也是一种可能,但由于要生成字符串,可能帮助不大。


内容的提问来源于stack exchange,提问作者Guilherme Iablonovski

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 07:22:03