如何用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
相关产品推荐
相关产品推荐

