You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Python中为多数据集拟合相同模型(对应R嵌套流程)

解决Polars嵌套数据集拟合GLM报错并提取目标系数

报错根源

核心问题:Polars嵌套列data的类型为List(Struct),statsmodels依赖的patsy公式解析器无法直接从该结构中提取字段(如recipe),会将整个嵌套结构视为单个Series元素,导致无法通过字段名访问数据。

分步解决方案

1. 编写自定义拟合函数

定义函数将嵌套子数据集转换为可被statsmodels识别的格式,拟合二项式GLM并提取recipeB的系数:

import polars as pl
import statsmodels.api as sm
import statsmodels.formula.api as smf

def extract_recipeB_coef(sub_data: list[dict]) -> float | None:
    # 将List(Struct)转换为Polars DataFrame
    sub_df = pl.DataFrame(sub_data)
    try:
        # 拟合二项式GLM,转换为Pandas DataFrame确保兼容性
        model = smf.glm(
            formula="events ~ recipe",
            data=sub_df.to_pandas(),
            family=sm.families.Binomial()
        ).fit()
        # 提取recipe[T.B]的系数,无对应项则返回None
        return model.params.get("recipe[T.B]", None)
    except Exception:
        # 处理子数据集样本不足、类别缺失等拟合失败情况
        return None

2. 在Polars中批量应用函数

使用map_elements对嵌套列data批量执行拟合逻辑:

# 假设你的嵌套数据集为df_nested,结构包含test_id和data列
df_result = df_nested.with_columns(
    pl.col("data").map_elements(extract_recipeB_coef).alias("recipeB_coef")
)

3. 性能优化替代方案(展开分组拟合)

若嵌套数据量较大,可先展开嵌套结构再分组拟合,避免map_elements的单元素处理开销:

# 展开嵌套的data列并解析Struct字段
df_exploded = df_nested.explode("data").unnest("data")

# 按test_id分组拟合模型的Pandas兼容写法
def group_fit(group):
    model = smf.glm(
        "events ~ recipe",
        data=group,
        family=sm.families.Binomial()
    ).fit()
    return model.params.get("recipe[T.B]", None)

# 分组拟合后转回Polars
df_result = pl.from_pandas(
    df_exploded.to_pandas().groupby("test_id").apply(group_fit).reset_index(name="recipeB_coef")
)

关键注意事项

  • 必须转换嵌套结构:List(Struct)无法直接被patsy解析,转换为DataFrame后才能让公式识别events、recipe等字段。
  • 兼容性处理:部分statsmodels版本对Polars DataFrame支持有限,转换为Pandas DataFrame可避免兼容性问题。
  • 异常捕获:子数据集可能存在样本量不足、类别缺失等问题,加入异常捕获可保证整个流程不中断。

内容的提问来源于stack exchange,提问作者Joe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 14:07:35