在Pandas中应用自定义函数修改列值及计算平均时间间隔
看起来你已经有了核心的解析思路,不过目前的代码在批量处理DataFrame行和实现最终平均值计算上还有点小问题,咱们一步步来解决:
处理Pandas中ISO时间间隔字符串并计算平均值
一、重构解析函数
你的parse函数目前只能返回单个值就提前退出,没法批量处理每行数据。咱们把它改成可以接收单个时间间隔字符串的独立函数,同时加入空值处理:
import re import pandas as pd def parse_timespan(timespan_str): # 处理空值或无效值的情况 if pd.isna(timespan_str) or not timespan_str.startswith(('-', 'P')): return 0 is_negative = timespan_str.startswith('-') # 根据正负截取需要匹配的字符串部分 target_str = timespan_str[1:] if is_negative else timespan_str # 用正则匹配年(Y)、月(M)、日(D)的数值 date_info = re.findall(r"P(?:(\d+)Y)?(?:(\d+)M)?(?:(\d+)D)?$", target_str) if not date_info: return 0 # 把匹配到的数值转成整数,空值则设为0 year, month, day = [int(num) if num else 0 for num in date_info[0]] # 计算总天数(这里用365天/年、30天/月是简化计算,你可以根据需求调整) daystotal = (year * 365) + (month * 30) + day return -daystotal if is_negative else daystotal
二、生成新列(不修改原DataFrame)
要在不改动原timespan列的前提下得到转换后的数值列,直接用Pandas的apply()方法就可以了——它会把函数应用到列的每一行,生成新列:
def do_process_citation_data(f_path): # 读取CSV时直接用skiprows=1跳过首行,比iloc[1:]更简洁 my_ocan = pd.read_csv( f_path, names=['oci', 'citing', 'cited', 'creation', 'timespan', 'journal_sc', 'author_sc'], skiprows=1, parse_dates=['creation'] # timespan是间隔字符串,不用解析为日期 ) # 统一转换creation列的日期格式 my_ocan['creation'] = pd.to_datetime(my_ocan['creation'], format="%Y-%m", yearfirst=True) # 生成新列timespan_days,完全保留原timespan列 my_ocan['timespan_days'] = my_ocan['timespan'].apply(parse_timespan) return my_ocan # 调用函数处理你的CSV文件 df = do_process_citation_data("citations.csv")
现在df里既有原始的timespan字符串列,又有新增的timespan_days整数列,完全不会修改原数据。
三、按年份计算timespan平均值
你的最终目标是计算特定年份的平均值,咱们可以先从creation列提取年份,再分组聚合:
1. 计算所有年份的平均值
# 从creation日期列提取年份,生成单独的year列 df['year'] = df['creation'].dt.year # 按年份分组,计算每个年份的timespan平均值 yearly_avg_timespan = df.groupby('year')['timespan_days'].mean() print(yearly_avg_timespan)
2. 计算单个特定年份的平均值
比如要计算1985年的平均值:
target_year = 1985 # 筛选出creation年份为1985的行,再计算平均值 avg_1985 = df[df['creation'].dt.year == target_year]['timespan_days'].mean() print(f"1985年的timespan平均值:{avg_1985}")
小提示
- 为什么不用手动循环?:Pandas的
apply是矢量化操作的简化写法,比手动写for循环效率高得多,尤其是处理几万行以上的数据时差距明显。 - 如果不想修改原DataFrame副本?:可以先复制一份原数据:
df_copy = df.copy(),然后在df_copy上生成新列,原df就完全不受影响。 - 日期计算更精准?:如果需要更精准的天数计算(比如考虑闰年、不同月份的天数差异),可以用
dateutil.relativedelta模块来解析ISO间隔字符串,不过你的简化计算已经能满足大部分场景需求啦。
内容的提问来源于stack exchange,提问作者Lisa Siurina
相关产品推荐
相关产品推荐

