Guest User

Untitled

a guest
Jun 13th, 2026
165
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 5.00 KB | None | 0 0
  1. # -*- coding: utf-8 -*-
  2. """
  3. 동일 득표쌍(collision) 분석 + 득표수 렌덤 노이즈(±1) 비교
  4. - 관측치에 무작위로 +1 또는 -1의 노이즈를 주었을 때 동일쌍 수가 어떻게 변하는지 비교합니다.
  5. """
  6. import json, sys, numpy as np
  7. from collections import defaultdict, Counter
  8. from itertools import combinations
  9. sys.stdout.reconfigure(encoding='utf-8')
  10.  
  11. rng = np.random.default_rng(20260603)
  12. recs = json.load(open('data/data_raw.json', encoding='utf-8'))
  13. print(f'총 레코드: {len(recs)}')
  14.  
  15. # ----------------------------------------------------
  16. # [추가] 득표수에 무작위로 +1 또는 -1을 더하는 함수
  17. # ----------------------------------------------------
  18. def apply_random_noise(records):
  19. perturbed_records = []
  20. for r in records:
  21. # 기존 record 복사
  22. new_r = r.copy()
  23. new_votes = []
  24. for v in r['votes']:
  25. if v is None:
  26. new_votes.append(None)
  27. continue
  28.  
  29. # 무작위로 +1 또는 -1 선택
  30. noise = rng.choice([-1, 1])
  31. perturbed_v = v + noise
  32.  
  33. # 득표수가 음수가 되지 않도록 방지
  34. if perturbed_v < 0:
  35. perturbed_v = 0
  36. new_votes.append(perturbed_v)
  37.  
  38. new_r['votes'] = new_votes
  39. perturbed_records.append(new_r)
  40. return perturbed_records
  41.  
  42. # 1. 원본 데이터 그룹화
  43. groups_original = defaultdict(list)
  44. for r in recs:
  45. groups_original[(r['contest'], r['gubun'])].append(r)
  46.  
  47. # 2. 노이즈가 추가된 데이터 생성 및 그룹화
  48. recs_perturbed = apply_random_noise(recs)
  49. groups_perturbed = defaultdict(list)
  50. for r in recs_perturbed:
  51. groups_perturbed[(r['contest'], r['gubun'])].append(r)
  52.  
  53. # ----------------------------------------------------
  54. # 기존 함수들 (그대로 유지)
  55. # ----------------------------------------------------
  56. def pair_count(values):
  57. c = Counter(values)
  58. pairs = sum(n * (n - 1) // 2 for n in c.values() if n >= 2)
  59. groups = sum(1 for n in c.values() if n >= 2)
  60. return pairs, groups
  61.  
  62. def topk_key(vec, ranks):
  63. sv = sorted(vec, reverse=True)
  64. if len(sv) <= max(ranks):
  65. return None
  66. return tuple(sv[r] for r in ranks)
  67.  
  68. # target_groups를 인자로 받도록 수정된 분석 함수
  69. def analyze_observed(gubun_filter, rankpairs, target_groups):
  70. out = {}
  71. for rp in rankpairs:
  72. pooled_vals = []
  73. per_contest_pairs = 0
  74. n_units = 0
  75. for (contest, gubun), rs in target_groups.items():
  76. if gubun != gubun_filter:
  77. continue
  78. vals = [topk_key(r['votes'], rp) for r in rs]
  79. vals = [v for v in vals if v is not None]
  80. n_units += len(vals)
  81. p, g = pair_count(vals)
  82. per_contest_pairs += p
  83. pooled_vals += vals
  84. pp, pg = pair_count(pooled_vals)
  85. out[rp] = dict(pooled_pairs=pp, pooled_groups=pg,
  86. per_contest_pairs=per_contest_pairs, n_units=n_units)
  87. return out
  88.  
  89. def full_vector_collisions(gubun_filter, target_groups):
  90. per_contest = 0
  91. for (contest, gubun), rs in target_groups.items():
  92. if gubun != gubun_filter:
  93. continue
  94. p, g = pair_count([tuple(r['votes']) for r in rs])
  95. per_contest += p
  96. return per_contest
  97.  
  98. # ----------------------------------------------------
  99. # 메인 실행부
  100. # ----------------------------------------------------
  101. if __name__ == '__main__':
  102. rankpairs = [(0, 1), (1, 2), (2, 3)]
  103.  
  104. for gubun in ['관내사전투표', '선거일투표']:
  105. print('\n' + '=' * 70)
  106. print(f'[{gubun}] 결과 비교')
  107. print('=' * 70)
  108.  
  109. # 원본 데이터 분석
  110. obs_orig = analyze_observed(gubun, rankpairs, groups_original)
  111. fv_orig = full_vector_collisions(gubun, groups_original)
  112.  
  113. # 노이즈 데이터 분석
  114. obs_pert = analyze_observed(gubun, rankpairs, groups_perturbed)
  115. fv_pert = full_vector_collisions(gubun, groups_perturbed)
  116.  
  117. for rp in rankpairs:
  118. o_orig = obs_orig[rp]
  119. o_pert = obs_pert[rp]
  120.  
  121. print(f'📌 순위쌍 {tuple(r+1 for r in rp)} (단위수={o_orig["n_units"]}):')
  122. print(f' - [원본] 지역구내 동일쌍: {o_orig["per_contest_pairs"]} | 전국풀링: {o_orig["pooled_pairs"]}')
  123. print(f' - [노이즈] 지역구내 동일쌍: {o_pert["per_contest_pairs"]} | 전국풀링: {o_pert["pooled_pairs"]}')
  124. print(f' - [변화량] 지역구내: {o_pert["per_contest_pairs"] - o_orig["per_contest_pairs"]} | 전국풀링: {o_pert["pooled_pairs"] - o_orig["pooled_pairs"]}')
  125. print('-' * 50)
  126.  
  127. print(f'✨ 전체 득표벡터 완전일치 쌍 (지역구 내):')
  128. print(f' - [원본]: {fv_orig} 쌍')
  129. print(f' - [노이즈]: {fv_pert} 쌍 (변화량: {fv_pert - fv_orig})')
Advertisement
Add Comment
Please, Sign In to add comment