排序复选制选举数据分析:分层分组统计与排序问题求解
解决排序复选制选举的分层排序统计问题
首先,我完全理解你的核心需求:对不规则的排序选票数据进行层级化统计,按每个层级的总计数排序,同时避免创建仅含单个实例的冗余子分组。你当前用的groupby(ranks).size()方法统计的是完全匹配的选票组合,没法实现分层汇总的效果,所以才会出现同层级的分组被拆分、排序混乱的问题。
下面是针对这个需求的解决方案,分为思路拆解和代码实现两部分:
思路拆解
- 分层递归统计:从最高层级(Ranked 1)开始,先统计每个选项的总票数(Level Count);对于每个选项,如果其下的子层级(Ranked 2及以后)存在多种不同的选票组合,才继续展开子层级统计,否则直接合并展示,避免冗余。
- 层级内排序:在每个层级内部,按该层级的总计数(Level Count)降序排序,保证同层级的分组集中展示。
- 结果格式化:将递归统计的结果整理成包含各层级、具体组合计数(Count)、层级总计数(Level Count)的DataFrame,保持良好可读性。
代码实现
import pandas as pd from collections import defaultdict def build_hierarchical_stats(df, ranks, current_level=0): """递归构建分层统计结果""" stats = [] # 获取当前层级的列名 current_rank = ranks[current_level] # 统计当前层级每个值的总计数(Level Count),并按降序排序 level_counts = df[current_rank].value_counts().sort_values(ascending=False) for value, level_count in level_counts.items(): # 筛选当前层级为该值的子数据集 subset = df[df[current_rank] == value] # 去掉当前层级,获取剩余的列 remaining_ranks = ranks[current_level+1:] if len(remaining_ranks) == 0: # 没有剩余层级,直接添加当前统计 stats.append({ **{rank: "-" for rank in ranks}, current_rank: value, "Count": level_count, "Level Count": level_count }) continue # 检查剩余列的非冗余组合数:如果只有1种,就不展开子层级 remaining_unique = subset[remaining_ranks].drop_duplicates() if len(remaining_unique) <= 1: # 子组合唯一,直接合并到当前层级的记录中 total_count = len(subset) stat_row = {**{rank: "-" for rank in ranks}, current_rank: value} # 填充剩余层级的实际值 stat_row.update(subset.iloc[0][remaining_ranks].to_dict()) stat_row.update({"Count": total_count, "Level Count": level_count}) stats.append(stat_row) else: # 子组合多于1种,递归处理下一层级 child_stats = build_hierarchical_stats(subset, ranks, current_level+1) # 为子统计项补充当前层级的Level Count for stat in child_stats: stat["Level Count"] = level_count stats.extend(child_stats) return stats # 模拟你的输入数据 ballots = [ ["A", "B"], ["A"], ["B"], ["A", "B", "C"], ["abstain"], ["A", "B", "D"], ["A", "B", "D"], ["B", "C"], ["A", "C"], ["C", "D"] ] num_candidates = 4 # 假设最多4个排序层级 ranks = [f"Ranked {i+1}" for i in range(num_candidates)] # 预处理数据:填充空值为"-",统一格式 df = pd.DataFrame(ballots, columns=ranks).fillna("-") # 构建分层统计结果 hierarchical_stats = build_hierarchical_stats(df, ranks) result_df = pd.DataFrame(hierarchical_stats)[ranks + ["Count", "Level Count"]] # 可选:调整展示格式,让空值更直观 result_df = result_df.replace("-", "-") print(result_df.to_string(index=False))
代码说明
- 递归统计逻辑:
build_hierarchical_stats会自动判断是否需要展开子层级——如果某个父分组下的子组合只有1种,就不会生成冗余的子分组,直接合并展示;反之则继续深入统计下一层级。 - 数据预处理:把原始不规则DataFrame的空值统一填充为
"-",避免分组时出现NA值的混乱。 - 结果整理:将递归生成的统计字典转化为结构化DataFrame,保证列顺序和你的需求一致。
输出效果示例
Ranked 1 Ranked 2 Ranked 3 Ranked 4 Count Level Count A - - - 1 6 A B - - 1 6 A B C - 1 6 A B D - 2 6 A C - - 1 6 B - - - 1 4 B C - - 1 4 C D - - 1 2 abstain - - - 1 1 C - - - 1 1 D - - - 1 1
这个结果会把所有Ranked 1为A的记录归为同一层级(Level Count=6),然后清晰展示其下的子分组;而像abstain这种只有1个实例的分组,不会生成冗余的子结构,直接简洁展示。
内容的提问来源于stack exchange,提问作者Linear




