Pandas read_csv的parse_dates未限制指定列,如何修复?
问题:CSV读取时时间列转换导致非时间列数据损坏
问题场景
读取CSV文件时,需要仅将指定时间列转换为纪元秒数,但现有代码错误地将转换逻辑应用到所有列,导致价格等数值列被损坏(如44.68这类原始值丢失)。
原代码
import pandas as pd TIME_STG = "Datum (UTC)" PRICE_STG = "Day Ahead Auktion (DE-LU)" PRICE_FILE = "booking_algorythm/data/energy-charts_Stromproduktion_und_Börsenstrompreise_in_Deutschland_2021.csv" def get_data(file, *columns): types_dict = {} parse_dates_list = [] for column in columns: if column == TIME_STG: types_dict.update({column: str}) parse_dates_list.append(column) else: types_dict.update({column: float}) data = pd.read_csv(file, sep=",", usecols=columns, dtype=types_dict, parse_dates=parse_dates_list, date_parser=lambda col: pd.to_datetime(col, utc=True)).astype(int) // 10**9 data_np = data.to_numpy() return data_np def get_price_vector(): data = get_data(PRICE_FILE, PRICE_STG, TIME_STG) return data def main(): vector = get_price_vector() print(vector) if __name__ == "__main__": main()
示例CSV数据
"Datum (UTC)","Kernenergie","Nicht Erneuerbar","Erneuerbar","Last","Day Ahead Auktion (DE-LU)" 2021-01-01T00:00:00.000Z,8151.12,35141.305,11491.71,43516.88,48.19 2021-01-01T00:15:00.000Z,8147.209,34875.902,11331.25,42998.01,48.19 2021-01-01T00:30:00.000Z,8154.02,34825.553,11179.375,42494.2,48.19 2021-01-01T00:45:00.000Z,8152.82,34889.11,11072.377,42320.32,48.19 2021-01-01T01:00:00.000Z,8156.53,34922.123,10955.356,41598.39,44.68 2021-01-01T01:15:00.000Z,8161.601,34856.2,10867.771,41214.32,44.68 2021-01-01T01:30:00.000Z,8158.36,35073.1,10789.049,40966.95,44.68 2021-01-01T01:45:00.000Z,8151.3,34972.501,10657.209,40664.63,44.68 2021-01-01T02:00:00.000Z,8145.589,34911.037,10637.605,40502.78,42.92
问题根源
原代码中.astype(int) // 10**9是对整个DataFrame执行操作,会把价格列的浮点值强制转为整数再除以1e9,直接破坏了原始价格数据。
解决方案
仅对时间列执行纪元秒数转换,其他列保留原有类型:
修改后的代码
import pandas as pd TIME_STG = "Datum (UTC)" PRICE_STG = "Day Ahead Auktion (DE-LU)" PRICE_FILE = "booking_algorythm/data/energy-charts_Stromproduktion_und_Börsenstrompreise_in_Deutschland_2021.csv" def get_data(file, *columns): types_dict = {} parse_dates_list = [] for column in columns: if column == TIME_STG: types_dict.update({column: str}) parse_dates_list.append(column) else: types_dict.update({column: float}) # 正常读取数据,不对整个DF做类型转换 data = pd.read_csv(file, sep=",", usecols=columns, dtype=types_dict, parse_dates=parse_dates_list, date_parser=lambda col: pd.to_datetime(col, utc=True)) # 仅转换时间列为纪元秒数(两种方式二选一即可) # 方式1:利用datetime64纳秒存储特性转换 data[TIME_STG] = (data[TIME_STG].astype(int) // 10**9) # 方式2:更直观的timestamp方法 # data[TIME_STG] = data[TIME_STG].apply(lambda x: x.timestamp()) data_np = data.to_numpy() return data_np def get_price_vector(): data = get_data(PRICE_FILE, PRICE_STG, TIME_STG) return data def main(): vector = get_price_vector() print(vector) if __name__ == "__main__": main()
说明
- 先完成CSV读取和时间列解析,得到包含datetime类型时间列、浮点类型价格列的DataFrame
- 单独对
TIME_STG列执行纪元秒数转换,两种方式均可:- 原方法:利用datetime64的纳秒存储特性,转整数后除以1e9得到秒数
timestamp()方法:直接调用pd.Timestamp的内置方法获取纪元秒数,可读性更强
- 价格等其他列保持原始浮点类型,不会被修改
- 最终转为numpy数组返回时,价格列保留48.19、44.68等原始值,时间列转为正确的纪元秒数
内容的提问来源于stack exchange,提问作者Andreas Schuldei
相关产品推荐
相关产品推荐

