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

如何将计数值DataFrame转换为箱线图所需的单个值数据?

问题描述

我有一个存储课程成绩计数值的DataFrame,结构如下:

Course #0432
180124231822288
31207532414430
1150142295211105
1811130198265105
31304525510024

对应的Pandas创建代码:

import pandas as pd

df = pd.DataFrame(
    {
        0: {"1801": 242, "3120": 75, "1150": 142, "1811": 130, "3130": 45},
        4: {"1801": 318, "3120": 324, "1150": 295, "1811": 198, "3130": 255},
        3: {"1801": 222, "3120": 144, "1150": 211, "1811": 265, "3130": 100},
        2: {"1801": 88, "3120": 30, "1150": 105, "1811": 105, "3130": 24},
    }
)
df.index.name = "Course #"

我想用Plotly绘制成绩分布的箱线图,但plotly.express.box需要单个值形式的数据(比如242个0、318个4这类展开后的列表)。目前我通过暴力循环的方式实现了转换,代码如下:

def transform_to_values(s: pd.DataFrame) -> pd.DataFrame:
    my_dict: dict[str, list[float]] = {}
    for value in s.itertuples():
        my_list: list[float] = []
        my_list.extend([4.0] * value._4)
        my_list.extend([3.0] * value._3)
        my_list.extend([2.0] * value._2)
        # 原数据无1分对应的列,注释该行
        # my_list.extend([1.0] * value.D)
        my_list.extend([0.0] * value._0)
        my_dict[value.Index] = my_list

    df = pd.DataFrame.from_dict(my_dict, orient="index").T

    return df

想请教有没有更简便的数据转换方法,或是适配箱线图函数的更优输入方式?

解决方案

方法一:用Pandas内置方法高效转长格式

无需手动循环,借助melt和repeat快速展开数据:

# 转长格式,保留课程号、成绩、计数
melted = df.reset_index().melt(id_vars="Course #", var_name="Score", value_name="Count")
# 根据计数值重复行,得到展开后的单个成绩数据
expanded = melted.loc[melted.index.repeat(melted["Count"])].drop("Count", axis=1)
# 转换成绩为数值类型
expanded["Score"] = expanded["Score"].astype(float)

直接将expanded传入Plotly Express:

import plotly.express as px

fig = px.box(expanded, x="Course #", y="Score")
fig.show()

方法二:用Plotly Graph Objects直接处理计数数据(无展开)

如果数据量较大,展开会占用过多内存,可手动计算箱线图统计量后绘制:

import plotly.graph_objects as go
import numpy as np

fig = go.Figure()

for course in df.index:
    # 获取当前课程的成绩值和对应计数
    scores = df.loc[course].index.astype(float)
    counts = df.loc[course].values
    # 构造加权成绩数组用于计算统计量
    weighted_scores = np.repeat(scores, counts)
    # 计算箱线图所需统计指标
    q1 = np.percentile(weighted_scores, 25)
    median = np.median(weighted_scores)
    q3 = np.percentile(weighted_scores, 75)
    iqr = q3 - q1
    min_val = max(np.min(weighted_scores), q1 - 1.5*iqr)
    max_val = min(np.max(weighted_scores), q3 + 1.5*iqr)
    
    # 添加箱线图轨迹
    fig.add_trace(go.Box(
        y=[median, q1, q3, min_val, max_val],
        name=str(course),
        boxpoints=False  # 不显示原始数据点
    ))

fig.update_layout(xaxis_title="Course #", yaxis_title="Score")
fig.show()

方案对比

  • 方法一代码简洁,适配Plotly Express的API,适合数据量较小的场景;
  • 方法二避免了数据展开的内存消耗,适合大数据集,手动计算统计量保证性能。

内容的提问来源于Stack Exchange,提问作者Andrew J Bonham

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 09:28:13