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

如何将时序得分数据集划分为每日均值相近的三个子集?

分组策略:让多日期均值相近的3组划分方案

针对将时间序列得分数据集划分为3组、使每组各日期均值尽可能接近的需求,以下是几种可行的Python实现思路,涵盖暴力解法(小数据集)和启发式近似解法(大数据集):


一、暴力解法(仅适用于小数据集)

如果你的数据集样本量很小(比如n≤10),可以直接枚举所有可能的分组组合,筛选出损失最小的方案。核心思路是遍历每个样本分到3组的所有可能,计算每组各日期的均值差异,选择最优解。

实现代码

import pandas as pd
import itertools
import numpy as np

# 示例数据集
data = pd.DataFrame({
    'id': [0,1,2,3],
    'day1': [0,0,0,0],
    'day2': [0.5,0.25,0,0.5],
    'day3': [1,0.75,0.5,1],
    'day4': [3,2.25,2.75,2]
})
scores = data.drop('id', axis=1).values
n_samples = len(scores)
n_groups = 3

# 定义损失函数:所有日期的组间均值方差之和(值越小,均值越接近)
def calculate_loss(group_assignments):
    group_sums = np.zeros((n_groups, scores.shape[1]))
    group_counts = np.zeros(n_groups)
    for idx, g in enumerate(group_assignments):
        group_sums[g] += scores[idx]
        group_counts[g] += 1
    # 跳过有空组的情况
    if 0 in group_counts:
        return float('inf')
    group_means = group_sums / group_counts[:, np.newaxis]
    # 计算每个日期的组间方差,求和得到总损失
    date_variances = np.var(group_means, axis=0)
    return date_variances.sum()

best_loss = float('inf')
best_assignment = None

# 枚举所有分组可能(仅适合小样本)
for assignment in itertools.product(range(n_groups), repeat=n_samples):
    current_loss = calculate_loss(assignment)
    if current_loss < best_loss:
        best_loss = current_loss
        best_assignment = assignment

# 输出结果
data['group'] = best_assignment
print("最优分组结果:")
print(data)
print(f"总损失值:{best_loss:.4f}")

局限性

当样本量超过15时,3^15=1400万+的组合数会导致计算时间急剧增加,仅适合极小数据集验证结果。


二、启发式近似解法(适合大数据集)

对于中等或大规模数据集,以下几种启发式方法可以在可接受的时间内得到优质近似解:

1. 贪心迭代优化(交换样本)

基于随机初始分组,通过迭代移动样本到其他组来降低损失,直到无法优化为止,是对随机划分的直接改进。

实现代码

import pandas as pd
import numpy as np

data = pd.DataFrame({
    'id': [0,1,2,3],
    'day1': [0,0,0,0],
    'day2': [0.5,0.25,0,0.5],
    'day3': [1,0.75,0.5,1],
    'day4': [3,2.25,2.75,2]
})
scores = data.drop('id', axis=1).values
n_samples = len(scores)
n_groups = 3

# 初始化分组:随机分配,确保每组非空
group_assignments = np.random.randint(0, n_groups, size=n_samples)
while len(set(group_assignments)) < n_groups:
    group_assignments = np.random.randint(0, n_groups, size=n_samples)

# 预计算初始组总和与样本数
group_sums = np.zeros((n_groups, scores.shape[1]))
group_counts = np.zeros(n_groups, dtype=int)
for idx, g in enumerate(group_assignments):
    group_sums[g] += scores[idx]
    group_counts[g] += 1

def get_current_loss():
    group_means = group_sums / group_counts[:, np.newaxis]
    return np.var(group_means, axis=0).sum()

best_loss = get_current_loss()
improved = True
iterations = 0

# 迭代优化
while improved and iterations < 1000:
    improved = False
    iterations += 1
    for idx in range(n_samples):
        current_group = group_assignments[idx]
        # 尝试将当前样本移到其他组
        for target_group in range(n_groups):
            if target_group == current_group:
                continue
            # 临时更新组统计数据
            temp_sums = group_sums.copy()
            temp_counts = group_counts.copy()
            temp_sums[current_group] -= scores[idx]
            temp_counts[current_group] -= 1
            temp_sums[target_group] += scores[idx]
            temp_counts[target_group] += 1
            # 计算临时损失
            temp_means = temp_sums / temp_counts[:, np.newaxis]
            temp_loss = np.var(temp_means, axis=0).sum()
            # 如果损失降低则保留移动
            if temp_loss < best_loss:
                best_loss = temp_loss
                group_sums = temp_sums
                group_counts = temp_counts
                group_assignments[idx] = target_group
                improved = True
                break
        if improved:
            continue

# 输出结果
data['group'] = group_assignments
print("迭代优化后的分组结果:")
print(data)
print(f"总损失值:{best_loss:.4f}")

2. 遗传算法(适合复杂场景)

通过模拟自然选择过程,迭代优化分组方案,适合对近似解质量要求较高的场景。需要安装deap库:pip install deap

实现代码

import pandas as pd
import numpy as np
from deap import base, creator, tools, algorithms

data = pd.DataFrame({
    'id': [0,1,2,3],
    'day1': [0,0,0,0],
    'day2': [0.5,0.25,0,0.5],
    'day3': [1,0.75,0.5,1],
    'day4': [3,2.25,2.75,2]
})
scores = data.drop('id', axis=1).values
n_samples = len(scores)
n_groups = 3

# 创建遗传算法核心类:最小化损失
creator.create("FitnessMin", base.Fitness, weights=(-1.0,))
creator.create("Individual", list, fitness=creator.FitnessMin)

toolbox = base.Toolbox()
toolbox.register("attr_group", np.random.randint, 0, n_groups)
toolbox.register("individual", tools.initRepeat, creator.Individual, toolbox.attr_group, n=n_samples)
toolbox.register("population", tools.initRepeat, list, toolbox.individual)

# 定义适应度计算(即损失函数)
def evaluate(individual):
    group_sums = np.zeros((n_groups, scores.shape[1]))
    group_counts = np.zeros(n_groups, dtype=int)
    for idx, g in enumerate(individual):
        group_sums[g] += scores[idx]
        group_counts[g] += 1
    # 空组惩罚
    if len(set(individual)) < n_groups:
        return (float('inf'),)
    group_means = group_sums / group_counts[:, np.newaxis]
    loss = np.var(group_means, axis=0).sum()
    return (loss,)

toolbox.register("mate", tools.cxTwoPoint)
toolbox.register("mutate", tools.mutUniformInt, low=0, up=n_groups-1, indpb=0.1)
toolbox.register("select", tools.selTournament, tournsize=3)
toolbox.register("evaluate", evaluate)

def main():
    pop = toolbox.population(n=50)  # 种群大小
    hof = tools.HallOfFame(1)  # 保存最优个体
    stats = tools.Statistics(lambda ind: ind.fitness.values)
    stats.register("avg", np.mean)
    stats.register("min", np.min)

    # 运行遗传算法
    pop, log = algorithms.eaSimple(pop, toolbox, cxpb=0.5, mutpb=0.2, ngen=100, stats=stats, halloffame=hof, verbose=True)

    # 输出结果
    best_assignment = hof[0]
    data['group'] = best_assignment
    print("\n遗传算法最优分组结果:")
    print(data)
    print(f"总损失值:{evaluate(best_assignment)[0]:.4f}")

if __name__ == "__main__":
    main()

三、损失函数的可选调整

可以根据实际需求替换损失函数:

  • 组间方差之和:默认方案,衡量均值的离散程度
  • 组均值与全局均值的平方差之和:让每组均值尽可能贴近整体平均水平
  • 最大组间差异:最小化所有日期中"最大均值-最小均值"的最大值,适合要求均值绝对接近的场景

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 01:10:12