高效筛选满足受访者数量要求的问卷问题组合
高效找出满足条件的问卷问题组
问题背景
我有一个存储问卷编码答案的DataFrame,每行对应一位受访者,每列对应一个问题。由于问卷存在跳题逻辑,并非所有受访者都回答了相同问题,因此这是一个包含NaN的稀疏DataFrame。示例数据及生成代码如下:
import pandas as pd import numpy as np # 创建示例DataFrame prng = np.random.RandomState(42) df = pd.DataFrame(prng.randint(0,5, size=(10, 3)), columns=['Q1','Q2','Q3']) # 随机添加NaN模拟跳题 df = df.mask(np.random.random(df.shape) < .3) print(df)
输出的示例DataFrame:
Q1 Q2 Q3 0 3.0 4.0 2.0 1 4.0 4.0 NaN 2 2.0 NaN 2.0 3 4.0 NaN 2.0 4 4.0 NaN 3.0 5 NaN 3.0 4.0 6 0.0 NaN NaN 7 4.0 3.0 NaN 8 0.0 NaN 2.0 9 NaN 3.0 3.0
实际场景中,这个DataFrame规模约为40000行、700列,需要找出至少包含n个问题、且被至少x位受访者全部回答的问题组。
基础方法的局限性
原尝试用itertools.combinations生成所有列的组合,再逐个计算符合条件的受访者数量。但对于700列的数据集,全量组合数是天文数字(仅2列组合就有C(700,2)=244650个,n越大组合数爆炸式增长),完全无法处理大规模数据。
高效解决方案
核心思路
基于受访者实际回答的问题集合来统计子集,避免遍历所有无意义的列组合:
- 提取每位受访者实际回答的问题集合(过滤掉NaN对应的列)
- 统计每个问题集合的出现次数
- 对每个问题集合,生成其所有长度≥n的子集,并累加每个子集的受访者数量
- 筛选出受访者数量≥x的子集
代码实现
import pandas as pd import numpy as np from itertools import combinations from collections import defaultdict # 生成示例数据(实际场景可直接读入你的DataFrame) prng = np.random.RandomState(42) df = pd.DataFrame(prng.randint(0,5, size=(10, 3)), columns=['Q1','Q2','Q3']) df = df.mask(np.random.random(df.shape) < .3) # 1. 提取每位受访者回答的问题集合 respondent_sets = [] for _, row in df.iterrows(): answered = row.dropna().index.tolist() if answered: respondent_sets.append(frozenset(answered)) # 2. 统计每个问题集合的出现次数 set_counter = defaultdict(int) for s in respondent_sets: set_counter[s] += 1 # 3. 统计所有符合长度要求的子集的受访者数量 min_num_questions = 2 subset_counter = defaultdict(int) for question_set, cnt in set_counter.items(): set_len = len(question_set) if set_len < min_num_questions: continue # 生成该集合所有长度≥min_num_questions的子集 for k in range(min_num_questions, set_len + 1): for subset in combinations(question_set, k): subset_counter[subset] += cnt # 4. 筛选出满足受访者数量要求的组 min_num_respondents = 3 final_groups = [ (subset, count) for subset, count in subset_counter.items() if count >= min_num_respondents ] # 按受访者数量降序、问题数量升序排序 final_groups.sort(key=lambda x: (-x[1], len(x[0]))) print(final_groups)
输出结果:
[(('Q1', 'Q3'), 5), (('Q1', 'Q2'), 3), (('Q2', 'Q3'), 3)]
性能优化说明
- 仅基于实际存在的回答集合生成子集,跳过了所有无受访者完整回答的列组合,大幅减少计算量
- 若受访者平均回答的问题数量远小于总列数(700),则子集生成的开销会非常可控
- 可通过
multiprocessing模块并行处理不同的问题集合,进一步提升大数据集下的处理速度
内容的提问来源于stack exchange,提问作者gabboshow
相关产品推荐
相关产品推荐

