如何在Pandas中拆分DataFrame、处理并合并?索引分组拓展特征方法
当然有!在Pandas里处理这种「按索引/分组键拆分数据、计算新特征再合并回原结构」的需求,有好几套顺手的方案,完全不用手动拆分组再拼接,我结合你的NYC GreenTaxi场景给你捋捋~
方法1:用groupby().transform()(最推荐,适合单特征广播回原行)
如果你的预定义函数是对每个分组计算单个值(比如每个上车点ID对应的行政区名称),然后要把这个值对应到原DataFrame的每一行,transform绝对是最优解——它会自动把分组计算的结果扩展成和原数据一样的长度,直接新增列就行,完全不用操心合并的事儿。
举个贴合你场景的例子:
import pandas as pd from geopy.geocoders import Nominatim # 定义你的地址查询函数(这里简化逻辑,实际替换成真实的geopy查询) def get_neighborhood(location_id): geolocator = Nominatim(user_agent="green_taxi_geo_app") # 假设你有location_id和经纬度的映射,或者直接查询(注意API调用限制) # 这里返回模拟结果,实际替换成真实查询逻辑 return f"Brooklyn Area {location_id}" # 加载GreenTaxi数据(假设你已经下载好) df = pd.read_parquet("./green_tripdata_2023-01.parquet") # 一行代码生成新特征! df["pickup_neighborhood"] = df.groupby("PULocationID")["PULocationID"].transform(get_neighborhood)
这里groupby("PULocationID")按上车地点ID分组,transform(get_neighborhood)会对每个分组的ID应用函数,然后自动把结果匹配回原DataFrame的每一行,索引完全对应,省心到爆。
方法2:用groupby().apply() + 自动合并(适合返回多特征/复杂结果)
如果你的函数需要对整个分组的DataFrame做处理,返回带多个新特征的分组数据(比如每个上车点返回行政区、邮编、区域类型),那用apply就很合适——Pandas会自动把所有处理后的分组拼接回原结构。
例子:
def process_location_group(group_df): # 获取当前分组的上车点ID loc_id = group_df["PULocationID"].iloc[0] geolocator = Nominatim(user_agent="green_taxi_geo_app") # 模拟查询得到多维度信息 neighborhood = f"Queens Area {loc_id}" zip_code = f"113{loc_id % 100}" area_type = "Residential" if loc_id < 100 else "Commercial" # 给当前分组添加新列 group_df["pickup_neighborhood"] = neighborhood group_df["pickup_zip"] = zip_code group_df["pickup_area_type"] = area_type return group_df # 应用函数,自动合并回原结构 processed_df = df.groupby("PULocationID", group_keys=False).apply(process_location_group) # processed_df就是带新特征的完整数据,索引和原数据完全一致,直接用就行!
加个group_keys=False是为了避免生成多余的分组键列,让结果更干净。
方法3:预计算映射表 + map()(适合耗时的API/数据库查询)
如果你的函数调用比较耗时(比如geopy的API查询,频繁调用容易被限流或者变慢),那最好先预计算所有唯一分组键的结果,再用map映射到原数据——这样能减少重复调用次数,效率提升一大截。
例子:
# 先提取所有唯一的上车点ID unique_pu_ids = df["PULocationID"].unique() # 预计算每个ID对应的结果,存在字典里 location_info_map = {} geolocator = Nominatim(user_agent="green_taxi_geo_app") for loc_id in unique_pu_ids: # 实际查询逻辑(记得加超时、缓存哦) location_info_map[loc_id] = { "neighborhood": f"Bronx Area {loc_id}", "zip_code": f"104{loc_id % 100}" } # 用map快速生成新列 df["pickup_neighborhood"] = df["PULocationID"].map(lambda x: location_info_map[x]["neighborhood"]) df["pickup_zip"] = df["PULocationID"].map(lambda x: location_info_map[x]["zip_code"])
这种方法特别适合需要外部调用的场景,比如查地理信息、查数据库,只需要查询一次每个唯一值,比分组后每次调用函数高效太多。
小提醒
- 如果你的函数需要用到分组里的多列数据,
transform也支持,比如计算每个上车点的平均行程时间,然后加到每一行:df["avg_pu_trip_time"] = df.groupby("PULocationID")["trip_duration"].transform("mean") - 用geopy的时候,记得设置
timeout参数,避免请求超时,也可以用functools.lru_cache缓存查询结果,进一步减少重复请求。
内容的提问来源于stack exchange,提问作者Sandeep R Venkatesh

