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

如何用Python的Pandas为CSV添加FedFY财年列并填充值?

问题描述

我正在写一段Python脚本,用来在合并CSV文件前做数据清洗,现在需要添加一个FedFY(联邦财年)列。现有数据里的DiscoveredDate列格式是10/1/2017 12:49这种字符串格式,需要按照以下规则填充FedFY:

  • 日期在2016年10月1日至2017年10月1日范围内的行,填充2017
  • 日期在2017年10月1日至2018年10月1日范围内的行,填充2018

我尝试了两种写法都不对,当前的脚本片段(省略了其他清洗步骤)如下:

import os
import re
import pandas as pd
import Tkinter
import numpy as np
outpath = os.path.join(os.getcwd(), "CSV Altered")
# TK asks user what file to assimilate from
from Tkinter import Tk
from tkFileDialog import askopenfilename
Tk().withdraw()
filepath = askopenfilename()
# show an "Open" dialog box and return the path to the selected file
#Filepath is acknowledged and disseminated with the following totally human protocols
filenames = os.path.basename(filepath)
filename = [filenames]
for f in filename:
    name = f
    df = pd.read_csv(f)
    # Make Longitude values negative if they aren't already.
    df['Longitude'] = - df['Longitude'].abs()
    # Add Federal Fiscal Year Field (FedFY)
    df['FedFY'] = df['DiscoveredDate']
    df['FedFY'] = df['FedFY'].replace({df['FedFY'].date_range(10/1/2016 1:00,10/1/2017 1:00): "2017",df['FedFY'].date_range(10/1/2017 1:00, 10/1/2018 1:00): "2018"})

我还试过下面这种写法,感觉完全不对:

for rows in df['FedFY']:
    if rows = df['FedFY'].dat...

请帮我指导怎么正确实现这个需求?


解决方案

嘿,我来帮你搞定这个问题!你的核心问题主要有两个:一是没把字符串格式的DiscoveredDate转换成Pandas能识别的datetime类型,二是用了错误的方式来匹配日期范围。下面一步步给你修正:

第一步:先把字符串日期转成datetime类型

Pandas根本没法直接对字符串做日期范围判断,所以这是必须要做的第一步:

# 读取CSV之后立刻转换日期列
df['DiscoveredDate'] = pd.to_datetime(df['DiscoveredDate'], format='%m/%d/%Y %H:%M')

这里的format参数要和你的日期格式完全对应:%m是月份(比如10),%d是日期(比如1),%Y是四位年份,%H:%M是小时和分钟,完美匹配你给出的10/1/2017 12:49格式。

第二步:生成FedFY列

有两种简单又高效的方式,选你喜欢的就行:

方法一:用pd.cut(推荐,以后加更多财年也方便)

pd.cut可以把连续的时间戳分成不同区间,直接给每个区间贴标签,非常适合这种分档需求:

# 先定义财年的分界日期
fy_bins = [pd.to_datetime('2016-10-01'), pd.to_datetime('2017-10-01'), pd.to_datetime('2018-10-01')]
# 对应每个区间的财年标签
fy_labels = ['2017', '2018']

# 生成FedFY列
df['FedFY'] = pd.cut(df['DiscoveredDate'], bins=fy_bins, labels=fy_labels, include_lowest=True)

include_lowest=True是为了让第一个区间包含左边界——也就是2016年10月1日当天的日期会被分到2017财年,完全符合你的规则。

方法二:用条件判断(适合只有两个区间的简单场景)

如果以后不会加更多财年,用numpy.where写条件判断更直观:

# 先定义2018财年的起始日期
fy_2018_start = pd.to_datetime('2017-10-01')

# 一句话搞定:日期早于2017-10-01就填2017,否则填2018
df['FedFY'] = np.where(df['DiscoveredDate'] < fy_2018_start, '2017', '2018')

整合到你的脚本里的完整片段

把上面的步骤加进去,你的代码这部分就变成这样了:

for f in filename:
    name = f
    df = pd.read_csv(f)
    # 处理Longitude列
    df['Longitude'] = - df['Longitude'].abs()
    # 转换日期列
    df['DiscoveredDate'] = pd.to_datetime(df['DiscoveredDate'], format='%m/%d/%Y %H:%M')
    # 添加FedFY列(这里用pd.cut的版本,你也可以换成numpy.where的)
    fy_bins = [pd.to_datetime('2016-10-01'), pd.to_datetime('2017-10-01'), pd.to_datetime('2018-10-01')]
    fy_labels = ['2017', '2018']
    df['FedFY'] = pd.cut(df['DiscoveredDate'], bins=fy_bins, labels=fy_labels, include_lowest=True)

为啥你之前的写法不对?

  • 你直接把DiscoveredDate赋值给FedFY,这时候它还是字符串类型,根本没法调用date_range方法(这个方法只有datetime类型的序列才能用)。
  • 你尝试的循环遍历每行的方式虽然理论上能行,但Pandas最擅长的是向量化操作,循环不仅效率低,代码还容易写错,完全没必要~

内容的提问来源于stack exchange,提问作者Moldy Mushroom

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 04:03:35