【发布时间】:2017-07-16 17:13:48
【问题描述】:
说明
在读取大型 csv 文件(几百万行混合数据)时,我使用 pandas 的 read_csv 方法的 converters 参数来方便地传递将字符串转换为日期时间对象等的函数。
但是,与手动转换相应的列相比,使用转换器参数非常慢。
示例代码
为了说明,让我们使用 3 种不同的方法将字符串转换为日期时间对象:
- 转换器参数
- parse_dates/date_parser 参数
- 加载 csv 后手动
注意,这里从字符串到日期时间的转换是任意的。这可以用其他函数替换(除了没有特定的 parse_dates/date_parser 参数)。
import pandas as pd # 0.19.2 with python 3.5
# create dummy data
rows = 100000
data = {"dates": pd.date_range("2017-02-27 20:44:23", periods=rows, freq="S")}
# save as temporary file for timeit
pd.DataFrame(data).to_csv("dummy")
# define converters
def convert_datetime(series):
return pd.to_datetime(series, format="%Y-%m-%d %H:%M:%S")
现在,让我们看看 timeit (Ipython) 的比较:
%%timeit
df = pd.read_csv("dummy", converters={"dates": convert_datetime})
# 1 loop, best of 3: 7.76 s per loop
%%timeit
df = pd.read_csv("dummy", parse_dates=["dates"], date_parser=convert_datetime)
# 10 loops, best of 3: 125 ms per loop
%%timeit
df = pd.read_csv("dummy")
df["dates"] = convert_datetime(df["dates"])
# 10 loops, best of 3: 129 ms per loop
结果
转换器的版本大约比其他版本慢 60 倍。为了更好地理解这一点,我将 convert_datetime 函数包装到一个小装饰器类中来计算调用次数:
class Counter:
def __init__(self, func):
self.func = func
self.count = 0
def __call__(self, *args, **kwargs):
self.count += 1
return self.func(*args, **kwargs)
@Counter
def convert_datetime(series):
return pd.to_datetime(series, format="%Y-%m-%d %H:%M:%S")
它揭示了使用转换器的参数为每个单个值调用 convert_datetime 函数,而其他版本只调用一次转换器函数。这解释了性能缺陷。
问题
这是为什么呢?我希望传递给转换器参数的矢量化函数一次对所有值执行,而不是单独对每个值执行。
【问题讨论】:
标签: python performance csv pandas dataframe