使用pandas数据框进行简单线性回归

7

我希望检查一些实体(SysNr)的趋势

我拥有跨越3年(2014、2015、2016)的数据

我关注大量变量,但是将限制本问题仅针对一个变量 ('res_f_r')

我的数据框看起来像这样

d = [
    {'RegnskabsAar': 2014, 'SysNr': 1, 'res_f_r': 350000},
    {'RegnskabsAar': 2015, 'SysNr': 1, 'res_f_r': 400000},
    {'RegnskabsAar': 2016, 'SysNr': 1, 'res_f_r': 450000},
    {'RegnskabsAar': 2014, 'SysNr': 2, 'res_f_r': 350000},
    {'RegnskabsAar': 2015, 'SysNr': 2, 'res_f_r': 300000},
    {'RegnskabsAar': 2016, 'SysNr': 2, 'res_f_r': 250000},
]

df = pd.DataFrame(d)



   RegnskabsAar  SysNr  res_f_r
0          2014      1   350000
1          2015      1   400000
2          2016      1   450000
3          2014      2   350000
4          2015      2   300000
5          2016      2   250000

我希望您能为每个实体(SysNr)进行线性回归,并返回其斜率和截距。
以上内容的期望输出为:
   SysNr  intercept  slope
0      1     300000  50000
1      2     400000 -50000

有什么想法吗?
2个回答

5
所以我不知道为什么我们的拦截值会有差异(也许是我做错了,或者你给出的数据不是你希望处理的全部数据),但是我建议你使用 np.polyfit 或你选择的工具(scikit-learn, scipy.stats.linregress等)结合 groupby 和 apply 进行操作:
In [25]: df.groupby("SysNr").apply(lambda g: np.polyfit(g.RegnskabsAar, g.res_f_r, 1))
Out[25]:
SysNr
1    [49999.99999999048, -100349999.99998075]
2    [-49999.99999999045, 101049999.99998072]
dtype: object

之后,进行美化:
In [43]: df.groupby("SysNr").apply(
    ...:     lambda g: np.polyfit(g.RegnskabsAar, g.res_f_r, 1)).apply(
    ...:     pd.Series).rename(columns={0:'slope', 1:'intercept'}).reset_index()
Out[43]:
   SysNr    slope     intercept
0      1  50000.0 -1.003500e+08
1      2 -50000.0  1.010500e+08

编辑:

因为您在评论中询问如何处理某些 SysNr 的缺失年份,所以请直接删除那些 NaNs 以进行有效的线性回归。当然,您也可以根据需要将其填充为平均值或其他内容,但我认为这并不是很有帮助。

如果实体只有一个年份的数据,则无法对其应用有用的线性回归。但是,您可以(如果需要并适合您的情况,请提供更多数据信息)将其他实体的斜率外推到此实体,并计算截距。当然,对于这个问题,您必须对实体斜率的分布做出一些假设(例如线性,那么 sysNr 3 的斜率将为 -150000.0)。


2
我使用sklearn的linear_model.LinearRegression()得到了与您发现的相同的截距值(与OP不同)。 - 3novak
我找到的截距值来自Excel趋势线,显然是以2013年为基础,而你的数据则归零。无论哪种情况都可以,因为我主要是在寻找斜率。 - Henrik Poulsen

3

您还可以使用来自pandasgroupby与来自scipy.statslinregress进行IT技术相关内容翻译:

from scipy.stats import linregress

# groupby column
grouped = df.groupby('SysNr')

# https://dev59.com/Lm7Xa4cB1Zd3GeqPnj1Z#14775604
# apply linear regression to each group
result_df = pd.DataFrame(grouped.apply(lambda x: linregress(x['RegnskabsAar'], x['res_f_r']))).reset_index()

# https://dev59.com/KV0b5IYBdhLWcg3wJ-Uz#29550458
# expand result to each column
result_df[['slope', 'intercept', 'r_value', 'p_value', 'std_err']] = result_df[0].apply(pd.Series)

# drop initial column with all in one
del result_df[0]

result_df

结果:

   SysNr    slope    intercept  r_value       p_value  std_err
0      1  50000.0 -100350000.0      1.0  9.003163e-11      0.0
1      2 -50000.0  101050000.0     -1.0  9.003163e-11      0.0

在这个设定中,我该如何处理NaN值?有些实体仅在一两年内具有数据,其余则为NaN。我应该在SQL端删除它们,还是可以在linregress函数中处理它们? - Henrik Poulsen
你可以尝试使用 https://dev59.com/TWYr5IYBdhLWcg3wfKL_#13643460 中建议的掩码。 - niraj

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接