将日期转换为浮点数以用于Pandas数据框的线性回归
看起来在Pandas中使用普通最小二乘法(OLS)线性回归时,参数必须是浮点数。我的数据来源是一个名为“gameAct.csv”的CSV文件,格式如下:
date, city, players, sales
2014-04-28,London,111,1091.28
2014-04-29,London,100,1100.44
2014-04-28,Paris,87,1001.33
...
我想分析销售额是如何随着日期变化的(也就是时间推移时,销售额是怎么变化的)。我下面的代码似乎有问题,主要是因为日期没有被当作浮点数来处理。我希望能得到一些帮助,解决在Pandas中遇到的这个索引问题。
这是我目前的代码(虽然可以编译,但不工作):
import pandas as pd
from pandas import DataFrame, Series
import statsmodels.formula.api as sm
df = pd.read_csv('gameAct.csv')
df.columns = ['date', 'city', 'players', 'sales']
city_data = df[df['city'] == 'London']
result = sm.ols(formula = 'sales ~ date', data = city_data).fit()
当我改变城市的值时,得到的R^2值总是等于1,这显然是不对的。我也尝试过在定义dataframe df
时使用index_col = 0, parse_dates == True
,但没有成功。
我怀疑还有更好的方法来读取这样的CSV文件,以便进行基本的日期回归分析,以及更一般的时间序列分析。任何帮助、示例和资源都非常感谢!
另外,使用上面的代码,如果我把某个城市的日期索引转换成数组,这个数组里的值是这样的:
'\xef\xbb\xbf2014-04-28'
怎么才能对所有非销售参数进行AIC分析呢?(比如,结果可能表明销售额最线性地依赖于日期和城市)。
4 个回答
df.date.dt.total_seconds()
如果你的日期数据类型是 datetime64[ns]
,那么你可以使用 dt.total_seconds()
这个方法;它会返回一个数字,表示总秒数(小数形式)。
我对statsmodels的具体情况不太了解,不过这篇帖子列出了所有关于Python中日期和时间的转换。并不是所有的转换都是一一对应的,所以我经常参考这篇帖子 ;-)
获取浮点数格式的年份
我更喜欢一种日期格式,它可以在没有上下文的情况下被理解。因此,我选择了浮点数年份的表示方式。这里的好处是,这个解决方案可以在numpy
层面上运行,所以应该会很快。
import numpy as np
import pandas as pd
def dt64_to_float(dt64):
"""Converts numpy.datetime64 to year as float.
Rounded to days
Parameters
----------
dt64 : np.datetime64 or np.ndarray(dtype='datetime64[X]')
date data
Returns
-------
float or np.ndarray(dtype=float)
Year in floating point representation
"""
year = dt64.astype('M8[Y]')
# print('year:', year)
days = (dt64 - year).astype('timedelta64[D]')
# print('days:', days)
year_next = year + np.timedelta64(1, 'Y')
# print('year_next:', year_next)
days_of_year = (year_next.astype('M8[D]') - year.astype('M8[D]')
).astype('timedelta64[D]')
# print('days_of_year:', days_of_year)
dt_float = 1970 + year.astype(float) + days / (days_of_year)
# print('dt_float:', dt_float)
return dt_float
if __name__ == "__main__":
dates = np.array([
'1970-01-01', '2014-01-01', '2020-12-31', '2019-12-31', '2010-04-28'],
dtype='datetime64[D]')
df = pd.DataFrame({
'date': dates,
'number': np.arange(5)
})
df['date_float'] = dt64_to_float(df['date'].to_numpy())
print('df:', df, sep='\n')
print()
dt64 = np.datetime64( "2011-11-11" )
print('dt64:', dt64_to_float(dt64))
输出
df:
date number date_float
0 1970-01-01 0 1970.000000
1 2014-01-01 1 2014.000000
2 2020-12-31 2 2020.997268
3 2019-12-31 3 2019.997260
4 2010-04-28 4 2010.320548
dt64: 2011.8602739726027
对于这种回归分析,我通常会把日期或时间戳转换成从数据开始算起的天数整数。
这样做效果很好:
df = pd.read_csv('test.csv')
df['date'] = pd.to_datetime(df['date'])
df['date_delta'] = (df['date'] - df['date'].min()) / np.timedelta64(1,'D')
city_data = df[df['city'] == 'London']
result = sm.ols(formula = 'sales ~ date_delta', data = city_data).fit()
这种方法的好处在于,你可以清楚地知道回归分析中使用的单位是“天”,而自动转换可能会隐含使用其他单位,这样会导致线性模型中的系数让人困惑。此外,这种方法还允许你把多个在不同时间开始的销售活动的数据结合在一起进行回归分析(比如你想研究某个活动的效果与活动进行天数之间的关系)。如果你想测量一年中的某一天的趋势,可以把1月1日设为你的起始点。自己选择起始日期让你对这一切有了掌控。
还有证据表明,statsmodels支持来自pandas的时间序列。你也许可以将其应用于线性模型: http://statsmodels.sourceforge.net/stable/examples/generated/ex_dates.html
另外,简单提一下: 你应该能够直接从csv文件中自动读取列名,就像我发的示例代码那样。在你的例子中,我看到csv文件第一行的逗号之间有空格,导致列名变成了' date'。去掉这些空格,自动读取csv的表头应该就能正常工作了。