首页 > 其他分享 >从 Pandas 到 Polars 十四:在Polars中拟合线性模型

从 Pandas 到 Polars 十四:在Polars中拟合线性模型

时间:2024-07-13 22:29:46浏览次数:8  
标签:df self Pandas --- ols 拟合 Polars col pl

​线性模型是数据科学和机器学习的基石。它们易于理解且拟合速度快。有了这个出色的新Polars插件,你现在可以直接在Polars中拟合线性模型,包括Lasso回归和Ridge回归。
这项工作是由Amzy Rajab在这个github仓库中完成的。
如果你想跟着学习,第一步是安装这个插件。你可以通过以下命令来完成这个操作。

pip install polars-ols patsy

在这里,patsy包被用于解析公式(尽管我们在下面没有使用它)。


注册命名空间
我们使用polars-ols包来拟合模型。Polars-ols是一个Polars插件。当我们导入一个插件时,该插件会将其命名空间注册到Polars中。命名空间是一组在特定标题下收集的表达式,其工作方式类似于内置命名空间,如用于时间序列表达式的dt或用于字符串表达式的str。
我们首先导入Polars库和插件。

import polars as pl
import polars_ols as pls 

当我们导入一个插件时,该插件会将其命名空间注册到Polars中。之后,我们就可以访问该命名空间中的表达式,在这个例子中,这个命名空间被称为least_squares。


拟合线性模型
我们创建了一个DataFrame,其中包含目标列y,并使用两个预测列x1和x2对其进行回归。

df = pl.DataFrame(
    {
        "y": [1.16, -2.16, -1.57, 0.21, 0.22, 1.6, -2.11, -2.92, -0.86, 0.47],
        "x1": [0.72, -2.43, -0.63, 0.05, -0.07, 0.65, -0.02, -1.64, -0.92, -0.27],
        "x2": [0.24, 0.18, -0.95, 0.23, 0.44, 1.01, -2.08, -1.36, 0.01, 0.75],
    }
)
df.head()
shape: (5, 3)
┌───────┬───────┬───────┐
│ y     ┆ x1    ┆ x2    │
│ ---   ┆ ---   ┆ ---   │
│ f64   ┆ f64   ┆ f64   │
╞═══════╪═══════╪═══════╡
│ 1.16  ┆ 0.72  ┆ 0.24  │
│ -2.16 ┆ -2.43 ┆ 0.18  │
│ -1.57 ┆ -0.63 ┆ -0.95 │
│ 0.21  ┆ 0.05  ┆ 0.23  │
│ 0.22  ┆ -0.07 ┆ 0.44  │
└───────┴───────┴───────┘

我们首先拟合一个普通最小二乘(即普通的线性回归)模型。我们指定:
1.目标列为 pl.col("y")
2.使用 least_squares.ols 表达式表示的普通最小二乘模型
3.预测器作为 least_squares.ols 内的表达式列表
4.预测输出列的名称,使用 alias 指定
 

ols_expr = (
  pl.col("y")
  .least_squares.ols(
      pl.col("x1"), 
      pl.col("x2")
  )
  .alias("ols")
)

然后,我们可以通过将表达式传递给with_columns来添加一个包含预测值的列。

(
  df
  .with_columns(
      ols_expr
  )
)       
shape: (10, 4)
┌───────┬───────┬───────┬───────────┐
│ y     ┆ x1    ┆ x2    ┆ ols       │
│ ---   ┆ ---   ┆ ---   ┆ ---       │
│ f64   ┆ f64   ┆ f64   ┆ f32       │
╞═══════╪═══════╪═══════╪═══════════╡
│ 1.16  ┆ 0.72  ┆ 0.24  ┆ 0.940459  │
│ -2.16 ┆ -2.43 ┆ 0.18  ┆ -2.196536 │
│ -1.57 ┆ -0.63 ┆ -0.95 ┆ -1.55357  │
│ 0.21  ┆ 0.05  ┆ 0.23  ┆ 0.275953  │
│ 0.22  ┆ -0.07 ┆ 0.44  ┆ 0.366057  │
│ 1.6   ┆ 0.65  ┆ 1.01  ┆ 1.632354  │
│ -2.11 ┆ -0.02 ┆ -2.08 ┆ -2.07331  │
│ -2.92 ┆ -1.64 ┆ -1.36 ┆ -2.945234 │
│ -0.86 ┆ -0.92 ┆ 0.01  ┆ -0.889025 │
│ 0.47  ┆ -0.27 ┆ 0.75  ┆ 0.476734  │
└───────┴───────┴───────┴───────────┘

瞧!我们已经在ols列中得到了线性模型的预测值。


拟合正则化模型
该库还支持拟合正则化模型,如Lasso和Ridge回归。我们可以通过使用least_squares.lasso而不是least_squares.ols来拟合一个Lasso模型。同时,我们还需要指定alpha参数,该参数表示正则化的强度。

lasso_expr = (
    pl.col("y")
    .least_squares.lasso(
        pl.col("x1"), 
        pl.col("x2"), 
        alpha=0.0001, 
        add_intercept=True
    )
)

注意 - 我将Elastic Net模型的结果与Scikit-learn库的结果进行了比较,它们非常接近。


训练模型以在测试集上使用
在上面的例子中,我们在与训练模型相同的数据上进行了预测。但在实际应用中,我们通常希望在训练集上训练模型,然后将其应用于新数据。使用python-ols(可能是指Polars库中的OLS实现)可以实现这一点,因为我们也可以让它输出回归系数而不是预测值。
回到我们最初的普通最小二乘模型,我们可以通过在ols方法中设置mode="coefficients"来获取系数。

ols_coef_expr = (
    pl.col("y")
    .least_squares.ols(
        pl.col("x1"), 
        pl.col("x2"), 
        add_intercept=True,
        mode="coefficients"
    )
    .alias("ols_intercept")
)

系数以pl.Struct列的形式返回,我们通过解嵌套将其值作为单独的列获取。

(
    df
    .select(
        ols_coef_expr
    )
    .unnest("ols_intercept")
)
shape: (1, 3)
┌──────────┬──────────┬──────────┐
│ x1       ┆ x2       ┆ const    │
│ ---      ┆ ---      ┆ ---      │
│ f32      ┆ f32      ┆ f32      │
╞══════════╪══════════╪══════════╡
│ 0.977375 ┆ 0.987413 ┆ 0.000757 │
└──────────┴──────────┴──────────┘

既然我们已经有了系数,我们就可以用它们来预测新数据集上的目标变量。
为了使这个流程更加顺畅,我们可以采用经典的Scikit-learn方法,即具有fit和transform方法(尽管你也可以将其称为predict方法)。我们可以通过创建具有这些方法的线性回归类来实现这一点。

from typing import List
class LinearRegressor:
    def __init__(
        self,
        target_col:str="y",
        feature_cols:List[str]=["x1","x2"],
        model="ols",
        add_intercept:bool=False
    ):
        self.target_col = target_col
        self.feature_cols = [pl.col(feature) for feature in feature_cols]
        self.add_intercept = add_intercept
        if model == "ols":
            self.model_expr = (
            pl.col(self.target_col)
            .least_squares.ols(
                *self.feature_cols,
                mode="coefficients",
                add_intercept=self.add_intercept
            )
            .alias("coef")
        )

    def fit(self, X):
        # Fit the model and save the coefficients in a DataFrame
        self.coefficients_df = (
            X
            .select(
                self.model_expr
            )
            .unnest("coef")
        )
        self.coef_ = (
            self.coefficients_df
            .select(self.feature_cols)
            .melt()
        )
        if self.add_intercept:
            self.intercept_ = self.coefficients_df["const"][0]
        else:
            self.intercept_ = 0.0
        return self

    def transform(self, X: pl.DataFrame):
        # Make predictions using the saved coefficients
        return (
            X
            # Add a row index
            .with_row_index()
            .pipe(
                # Join the predictions
                lambda X: X.join(
                    # Select the predictor columns
                    X.select("index", *self.feature_cols)
                    # Melt (so we can join the coefficients)
                    .melt(id_vars="index",value_name="predictor")
                    .join(
                        # Join the coefficients
                    (
                        X
                        .select(
                            self.model_expr
                        )
                        .unnest("coef")
                        .melt(value_name="coef")
                    ),
                        on="variable",
                    )
                    # Multiply by the predictors
                    .with_columns(pred=(pl.col("predictor") * pl.col("coef")))
                    # Gather back up into rows
                    .group_by("index")
                    .agg(
                        pl.col("pred").sum() + self.intercept_
                    ),
                    on="index",
                )
            )
            .sort("index")
        )

基本思路是:
1.在fit中,我们创建之前看到的包含变量名和系数的DataFrame
2.在transform中,我们将特征矩阵X转换为长格式,以便我们可以根据变量名将系数与数据连接起来。然后,我们将系数乘以数据,并对同一行上的所有数据进行求和,以获取预测值。
然后,我们使用这个类对测试DataFrame进行预测,如下所示:

df_train, df_test = df[:7], df[7:]

linear_regressor = LinearRegressor(target_col="y",feature_cols=["x1","x2"],model="ols")

reg.fit(X=df_train)

reg.transform(X=df_test)
shape: (3, 5)
┌───────┬───────┬───────┬───────┬───────────┐
│ index ┆ y     ┆ x1    ┆ x2    ┆ pred      │
│ ---   ┆ ---   ┆ ---   ┆ ---   ┆ ---       │
│ u32   ┆ f64   ┆ f64   ┆ f64   ┆ f64       │
╞═══════╪═══════╪═══════╪═══════╪═══════════╡
│ 0     ┆ -2.92 ┆ -1.64 ┆ -1.36 ┆ -2.945234 │
│ 1     ┆ -0.86 ┆ -0.92 ┆ 0.01  ┆ -0.889025 │
│ 2     ┆ 0.47  ┆ -0.27 ┆ 0.75  ┆ 0.476734  │
└───────┴───────┴───────┴───────┴───────────┘

这只是使用polars-ols插件可以做的开始。我没有在这里涵盖的主题包括将数据拟合到不同的子组、在滚动窗口上拟合模型以及使用公式拟合模型。

标签:df,self,Pandas,---,ols,拟合,Polars,col,pl
From: https://blog.csdn.net/sosogod/article/details/140399659

相关文章

  • 算法金 | 来了,pandas 2.0
    大侠幸会,在下全网同名「算法金」0基础转AI上岸,多个算法赛Top「日更万日,让更多人享受智能乐趣」今日210+/10000,内含Pandas是一个强大的数据分析库,广泛应用于科学研究、金融分析、商业智能等领域。它提供了高效的数据结构和数据分析工具,使得处理和分析数据变得更加简......
  • pandas导出excel
    工具类cvsutil.py#!/usr/bin/envpython#-*-coding:utf-8-*-importcsvimportcodecsimportioclassUTF8Recoder:"""IteratorthatreadsanencodedstreamandreencodestheinputtoUTF-8"""def__init_......
  • pandas agg函数的详细介绍与应用
    pandasagg函数的详细介绍与应用参考:pandasaggPandas是一个强大的Python数据处理库,提供了广泛的方法来进行数据分析。其中,agg函数是一个非常有用的工具,它允许用户对数据进行多种聚合操作,可以极大地简化数据处理过程。本文将详细介绍agg函数的使用方法,并通过多个示例展示......
  • Python教程:Pandas数据转换编码的10种方式
    1.构建测试数据集importpandasaspdimportnumpyasnpdf=pd.DataFrame({'Sex':['M','F','M','M','M','F','M','F','F','F'],'Cou......
  • 通过傅里叶级数拟合曲线
    importnumpyasnpimportmatplotlib.pyplotaspltfromscipy.optimizeimportcurve_fitfilename='test.txt'#fourierseriesdefintionstau=0.045deffourier(x,*a):ret=a[0]*np.cos(np.pi/tau*x)fordeginrange(1,len(a)):ret......
  • matplotlib绘制拟合大象曲线
    通过十个参数绘制大象曲线importmatplotlib.pyplotaspltimportnumpyasnpimportmatplotlib.animationasanimationnum=101k=np.arange(1,6)t=np.linspace(0,2*np.pi,num)Ax=[0,0,12,0,-14]Bx=[50,18,0,0,0]Ay=[-60,0,0,0,0]By=[-30,8,-10,0,0]eye=20x=(np......
  • Python酷库之旅-第三方库Pandas(012)
    目录一、用法精讲28、pandas.HDFStore.keys函数28-1、语法28-2、参数28-3、功能28-4、返回值28-5、说明28-6、用法28-6-1、数据准备28-6-2、代码示例28-6-3、结果输出29、pandas.HDFStore.groups函数29-1、语法29-2、参数29-3、功能29-4、返回值29-5、说明29......
  • Python实战训练(方程与拟合曲线)
    1.方程求e^x-派(3.14)的解用二分法来求解,先简单算出解所在的区间,然后用迭代法求逼近解,一般不能得到精准的解,所以设置一个能满足自己进度的标准来判断解是否满足这里打印出解x0是因为在递归过程中没有变量去接收返回值,所以返回x0,再打印x0得到的是None,再用numpy自带的log(pi)就查......
  • Python酷库之旅-第三方库Pandas(011)
    目录一、用法精讲25、pandas.HDFStore.get函数25-1、语法25-2、参数25-3、功能25-4、返回值25-5、说明25-6、用法25-6-1、数据准备25-6-2、代码示例25-6-3、结果输出26、pandas.HDFStore.select函数26-1、语法26-2、参数26-3、功能26-4、返回值26-5、说明26-......
  • Pandas我这个填充nan值为什么填充不上呢?
    大家好,我是Python进阶者。一、前言前几天在Python钻石交流群【逆光】问了一个Python数据处理的问题,问题如下:请问一下,我这个填充nan值为什么填充不上呢二、实现过程这里【瑜亮老师】给了个思路如下:试试看这样,代码如下:sf_mergetotal.loc[sf_mergetotal['寄件人']=='钟李平',......