Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

노트북은 예측 → 계산 → 대조 세 부분으로 고정한다. 새 개념은 도입하지 않는다. 본문에서 이미 유도한 것을 수치로 확인할 뿐이다.

1. 예측 (코드를 쓰기 전에)

아래 칸을 먼저 채운다. 계산하지 않는다.

  1. 두 배 비율의 표본판. Pareto(ζ=1\zeta=1, xm=1x_m=1) 표본 2,000개(역함수 표본추출, seed 0)에서 경험적 생존함수의 두 배 비율 Fˉ^(2x)/Fˉ^(x)\hat{\bar F}(2x)/\hat{\bar F}(x)x=1,2,5,10,20x=1,2,5,10,20에서 구하면? 지수(λ=0.1\lambda=0.1) 표본에서는?

    예측: ____

  2. 순위–규모 기울기. 같은 두 표본의 log-log OLS 기울기를 전체 / 상위 10%(순위 1–200) / 순위 201 이후(나머지 1,800개)로 나누면?

    예측: ____

  3. 단위 변경. 멱 표본에 1,300을 곱하면(달러→원) log-log 기울기와 절편은? 지수 표본에 곱하면 λ^=1/xˉ\hat\lambda=1/\bar x는?

    예측: ____

2. 계산

패키지 호출로 답을 내지 않는다. 표본과 경험적 생존함수와 OLS 기울기를 직접 쌓는다.

import numpy as np
import matplotlib.pyplot as plt
from matplotlib import font_manager

# 한국어 글꼴을 후보 목록에서 있는 것으로 고른다
_cands = ['Apple SD Gothic Neo', 'AppleGothic', 'NanumGothic', 'Noto Sans CJK KR', 'Malgun Gothic']
_have = {f.name for f in font_manager.fontManager.ttflist}
for _f in _cands:
    if _f in _have:
        plt.rcParams['font.family'] = _f
        break
plt.rcParams['axes.unicode_minus'] = False

# 본문 3절과 같은 파라미터에서 두 표본을 역함수 표본추출로 쌓는다
ZETA, XM, LAM, DELTA, N = 1.0, 1.0, 0.1, 1.0, 2000
rng = np.random.default_rng(0)
u = rng.uniform(size=N)
x_par = XM * u ** (-1.0 / ZETA)   # 생존함수 (x/x_m)^{-ζ} 를 가진 표본
x_exp = -np.log(u) / LAM          # 생존함수 e^{-λx} 를 가진 표본

print('글꼴:', plt.rcParams['font.family'][0])
print(f'멱  표본  최소 {x_par.min():9.4f}  중앙값 {np.median(x_par):8.4f}  최대 {x_par.max():10.2f}')
print(f'지수 표본  최소 {x_exp.min():9.4f}  중앙값 {np.median(x_exp):8.4f}  최대 {x_exp.max():10.2f}')
print(f'표본평균  멱 {x_par.mean():.3f} (이론은 ζ=1에서 발산)   지수 {x_exp.mean():.3f} (이론 1/λ = 10)')
글꼴: Apple SD Gothic Neo
멱  표본  최소    1.0005  중앙값   2.0112  최대    5263.11
지수 표본  최소    0.0050  중앙값   6.9875  최대      85.68
표본평균  멱 13.727 (이론은 ζ=1에서 발산)   지수 10.189 (이론 1/λ = 10)

예측 1. 두 검사를 두 표본에 건다. 두 배 비율 Fˉ^(2x)/Fˉ^(x)\hat{\bar F}(2x)/\hat{\bar F}(x)+Δ+\Delta 비율 Fˉ^(x+1)/Fˉ^(x)\hat{\bar F}(x+1)/\hat{\bar F}(x)를 본문 3절 2×22\times2 표와 같은 자리(x=1,2,5,10,20x=1,2,5,10,20)에서 센다.

# 경험적 생존함수를 x보다 큰 표본의 비율로 직접 센다
def Fhat(x, t):
    return np.mean(x > t)


TS = [1.0, 2.0, 5.0, 10.0, 20.0]
par_dbl, exp_dbl = [], []
print('두 배 검사 — 경험적 생존함수의 비율')
print(f"{'x':>4}{'멱 표본':>12}{'멱 이론':>11}{'지수 표본':>13}{'지수 이론':>12}{'꼬리 표본수':>14}")
for t in TS:
    p = Fhat(x_par, 2 * t) / Fhat(x_par, t)
    e = Fhat(x_exp, 2 * t) / Fhat(x_exp, t)
    par_dbl.append(p)
    exp_dbl.append(e)
    print(f'{t:4.0f}{p:12.4f}{2.0 ** -ZETA:11.4f}{e:13.4f}{np.exp(-LAM * t):12.4f}'
          f'{int(np.sum(x_par > t)):7d}/{int(np.sum(x_exp > t)):4d}')
두 배 검사 — 경험적 생존함수의 비율
   x        멱 표본       멱 이론        지수 표본       지수 이론        꼬리 표본수
   1      0.5035     0.5000       0.9009      0.9048   2000/1806
   2      0.5045     0.5000       0.8181      0.8187   1007/1627
   5      0.5012     0.5000       0.6051      0.6065    419/1223
  10      0.5238     0.5000       0.3770      0.3679    210/ 740
  20      0.5636     0.5000       0.1828      0.1353    110/ 279
# 같은 표본에 +Δ 검사를 걸어 본문 3절 2x2 표의 표본판을 쌓는다
print(f"{'x':>4}{'멱 2배':>11}{'멱 +1':>10}{'지수 2배':>12}{'지수 +1':>11}")
for t in TS:
    print(f'{t:4.0f}'
          f'{Fhat(x_par, 2 * t) / Fhat(x_par, t):11.4f}'
          f'{Fhat(x_par, t + DELTA) / Fhat(x_par, t):10.4f}'
          f'{Fhat(x_exp, 2 * t) / Fhat(x_exp, t):12.4f}'
          f'{Fhat(x_exp, t + DELTA) / Fhat(x_exp, t):11.4f}')
print()
print(f'이론  멱 2배 = 2^-ζ = {2.0 ** -ZETA:.4f} 고정,   멱 +1 = (1+1/x)^-ζ — x가 남는다')
print(f'      지수 +1 = e^-λΔ = {np.exp(-LAM * DELTA):.4f} 고정,   지수 2배 = e^-λx — x가 남는다')
   x       멱 2배      멱 +1       지수 2배      지수 +1
   1     0.5035    0.5035      0.9009     0.9009
   2     0.5045    0.6663      0.8181     0.9041
   5     0.5012    0.8091      0.6051     0.8880
  10     0.5238    0.9238      0.3770     0.9054
  20     0.5636    0.9636      0.1828     0.9032

이론  멱 2배 = 2^-ζ = 0.5000 고정,   멱 +1 = (1+1/x)^-ζ — x가 남는다
      지수 +1 = e^-λΔ = 0.9048 고정,   지수 2배 = e^-λx — x가 남는다
# 두 검사의 표본 비율과 이론 곡선을 같은 축에 쌓는다
tg = np.linspace(1.0, 20.0, 39)
d_par = np.array([Fhat(x_par, 2 * t) / Fhat(x_par, t) for t in tg])
d_exp = np.array([Fhat(x_exp, 2 * t) / Fhat(x_exp, t) for t in tg])
a_par = np.array([Fhat(x_par, t + DELTA) / Fhat(x_par, t) for t in tg])
a_exp = np.array([Fhat(x_exp, t + DELTA) / Fhat(x_exp, t) for t in tg])

fig, axes = plt.subplots(1, 2, figsize=(9.4, 3.5))
axes[0].plot(tg, d_par, 'o', ms=3.2, color='#3b6ea5', label='멱 표본')
axes[0].plot(tg, np.full_like(tg, 2.0 ** -ZETA), color='#3b6ea5', lw=1.0, label=r'멱 이론 $2^{-\zeta}$')
axes[0].plot(tg, d_exp, 's', ms=3.2, color='#c0392b', label='지수 표본')
axes[0].plot(tg, np.exp(-LAM * tg), color='#c0392b', lw=1.0, label=r'지수 이론 $e^{-\lambda x}$')
axes[0].set_ylabel(r'$\hat{\bar F}(2x)/\hat{\bar F}(x)$')
axes[0].set_title('두 배 검사 — 멱만 수평선')
axes[0].legend(fontsize=8, loc='lower left')
axes[1].plot(tg, a_par, 'o', ms=3.2, color='#3b6ea5')
axes[1].plot(tg, (1.0 + DELTA / tg) ** -ZETA, color='#3b6ea5', lw=1.0)
axes[1].plot(tg, a_exp, 's', ms=3.2, color='#c0392b')
axes[1].plot(tg, np.full_like(tg, np.exp(-LAM * DELTA)), color='#c0392b', lw=1.0)
axes[1].set_ylabel(r'$\hat{\bar F}(x+1)/\hat{\bar F}(x)$')
axes[1].set_title(r'$+\Delta$ 검사 ($\Delta=1$) — 지수만 수평선')
for ax in axes:
    ax.set_xlabel('규모 $x$')
    ax.set_ylim(0.0, 1.05)
plt.tight_layout()
plt.show()
<Figure size 940x350 with 2 Axes>

예측 2. 표본을 내림차순으로 정렬해 순위를 붙이고, lnr\ln r에 대한 lnx\ln x의 OLS 기울기를 공분산/분산 비로 직접 만든다. 전체 · 상위 10%(순위 1–200) · 순위 201 이후(나머지 1,800개) 세 구간에서 각각 센다.

# 순위를 붙이고 공분산/분산으로 OLS 기울기를 직접 만든다 — 적합 함수를 쓰지 않는다
def ols(a, b):
    a = np.asarray(a, float)
    b = np.asarray(b, float)
    return np.mean((a - a.mean()) * (b - b.mean())) / np.mean((a - a.mean()) ** 2)


lr = np.log(np.arange(1, N + 1))
lx_par = np.log(np.sort(x_par)[::-1])
lx_exp = np.log(np.sort(x_exp)[::-1])
slopes = {}
print('순위-규모 log-log OLS 기울기')
print(f"{'표본':>5}{'전체':>12}{'상위 10%(1-200)':>19}{'순위 201 이후':>18}")
for name, lx in [('멱', lx_par), ('지수', lx_exp)]:
    slopes[name] = (ols(lr, lx), ols(lr[:200], lx[:200]), ols(lr[200:], lx[200:]))
    s = slopes[name]
    print(f'{name:>5}{s[0]:12.4f}{s[1]:14.4f}{s[2]:16.4f}')
print()
print('한 구간의 기울기만으로는 두 표본을 가르지 못한다 — 전체 기울기가 둘 다 -1 근처다')
순위-규모 log-log OLS 기울기
   표본          전체      상위 10%(1-200)         순위 201 이후
    멱     -1.0536       -1.1347         -1.0161
   지수     -1.0256       -0.2818         -1.7165

한 구간의 기울기만으로는 두 표본을 가르지 못한다 — 전체 기울기가 둘 다 -1 근처다
# 두 표본의 순위-규모를 ln-ln 평면에 찍는다
fig, ax = plt.subplots(figsize=(6.4, 4.2))
ax.plot(lr, lx_par, '.', ms=2.5, color='#3b6ea5',
        label='멱: 전체 {:.2f} · 상위 {:.2f} · 나머지 {:.2f}'.format(*slopes['멱']))
ax.plot(lr, lx_exp, '.', ms=2.5, color='#c0392b',
        label='지수: 전체 {:.2f} · 상위 {:.2f} · 나머지 {:.2f}'.format(*slopes['지수']))
ax.plot(lr, np.log(N) - lr, color='k', lw=0.9, ls='--', label=r'기울기 $-1$ 기준선')
ax.axvline(np.log(200.0), color='#2e7d5b', lw=0.8, ls=':')
ax.text(np.log(200.0) + 0.1, lx_par.max() - 0.4, '순위 200', color='#2e7d5b', fontsize=9)
ax.set_xlabel(r'$\ln$ 순위 $r$')
ax.set_ylabel(r'$\ln$ 규모 $x$')
ax.set_title('멱 표본은 직선, 지수 표본은 휜다')
ax.legend(fontsize=8, loc='lower left', title='구간별 OLS 기울기', title_fontsize=8)
plt.tight_layout()
plt.show()
<Figure size 640x420 with 1 Axes>

예측 3. 같은 표본에 1,300을 곱해(달러→원) 같은 계산을 반복한다. 지수 표본에서는 λ^=1/xˉ\hat\lambda=1/\bar x를 단위 변경 전후로 잰다.

# 표본에 1300을 곱해(달러→원) 기울기와 절편을 다시 쌓는다
C13 = 1300.0
unit = {}
print(f"{'표본':>5}{'기울기(달러)':>18}{'기울기(원)':>18}{'절편(달러)':>14}{'절편(원)':>12}{'절편 차':>11}")
for name, x in [('멱', x_par), ('지수', x_exp)]:
    l1 = np.log(np.sort(x)[::-1])
    l2 = np.log(np.sort(x * C13)[::-1])
    b1, b2 = ols(lr, l1), ols(lr, l2)
    a1 = l1.mean() - b1 * lr.mean()
    a2 = l2.mean() - b2 * lr.mean()
    unit[name] = (b1, b2, a1, a2)
    print(f'{name:>5}{b1:16.10f}{b2:17.10f}{a1:12.4f}{a2:11.4f}{a2 - a1:10.4f}')
print(f'ln 1300 = {np.log(C13):.4f}   기울기 차 = '
      f'{unit["멱"][1] - unit["멱"][0]:.1e}, {unit["지수"][1] - unit["지수"][0]:.1e}')
lam1 = 1.0 / x_exp.mean()
lam2 = 1.0 / (x_exp * C13).mean()
print(f'λ̂ = 1/x̄ : {lam1:.6f} → {lam2:.4e}   비 {lam2 / lam1:.10f}   1/1300 = {1.0 / C13:.10f}')
   표본           기울기(달러)            기울기(원)        절편(달러)       절편(원)       절편 차
    멱   -1.0536373357    -1.0536373357      7.9764    15.1465    7.1701
   지수   -1.0256361384    -1.0256361384      8.5044    15.6745    7.1701
ln 1300 = 7.1701   기울기 차 = 0.0e+00, 0.0e+00
λ̂ = 1/x̄ : 0.098143 → 7.5495e-05   비 0.0007692308   1/1300 = 0.0007692308

보너스. 배율 하나로는 부족하다는 것(가정 A3)을 반례로 확인한다. P(x)=x1.5(1+0.3cos(2πlog2x))P(x)=x^{-1.5}\bigl(1+0.3\cos(2\pi\log_2 x)\bigr)b=2b=2에서만 불변이다.

# 배율 2 하나만 통과하는 반례를 쌓아 가정 A3(모든 b)의 자리를 확인한다
def P_osc(x, g=1.5, e=0.3):
    return x ** (-g) * (1.0 + e * np.cos(2.0 * np.pi * np.log2(x)))


print('P(x) = x^-1.5 (1 + 0.3 cos(2π log2 x))')
print(f"{'x':>6}{'P(2x)/P(x)':>14}{'P(3x)/P(x)':>14}")
for xv in [1.3, 2.7, 10.0]:
    print(f'{xv:6.1f}{P_osc(2 * xv) / P_osc(xv):14.4f}{P_osc(3 * xv) / P_osc(xv):14.4f}')
print(f'이론  2^-1.5 = {2.0 ** -1.5:.4f} (맞는다)     3^-1.5 = {3.0 ** -1.5:.4f} (맞지 않는다)')
P(x) = x^-1.5 (1 + 0.3 cos(2π log2 x))
     x    P(2x)/P(x)    P(3x)/P(x)
   1.3        0.3536        0.3175
   2.7        0.3536        0.3440
  10.0        0.3536        0.2769
이론  2^-1.5 = 0.3536 (맞는다)     3^-1.5 = 0.1925 (맞지 않는다)
# 예측 항목 세 개에 대응하는 수치를 한자리에 모은다
print('[예측 1] 두 배 비율 x = 1, 2, 5, 10, 20')
print('   멱  표본 ' + ', '.join(f'{v:.3f}' for v in par_dbl) + f'   이론 {2.0 ** -ZETA:.3f} 고정')
print('   지수 표본 ' + ', '.join(f'{v:.3f}' for v in exp_dbl))
print('   지수 이론 ' + ', '.join(f'{np.exp(-LAM * t):.3f}' for t in TS))
print('[예측 2] log-log OLS 기울기 (전체 / 상위 10% / 순위 201 이후)')
for name in ['멱', '지수']:
    print(f'   {name:>4}  ' + ' / '.join(f'{s:.3f}' for s in slopes[name]))
print('[예측 3] 1300배 전후')
for name in ['멱', '지수']:
    b1, b2, a1, a2 = unit[name]
    print(f'   {name:>4}  기울기 {b1:.6f} → {b2:.6f} (차 {b2 - b1:+.1e}), '
          f'절편 {a1:.4f} → {a2:.4f} (차 {a2 - a1:.4f})')
print(f'   λ̂  {lam1:.6f} → {lam2:.4e}  ({lam2 / lam1:.3e} 배 = 1/1300)')
[예측 1] 두 배 비율 x = 1, 2, 5, 10, 20
   멱  표본 0.503, 0.504, 0.501, 0.524, 0.564   이론 0.500 고정
   지수 표본 0.901, 0.818, 0.605, 0.377, 0.183
   지수 이론 0.905, 0.819, 0.607, 0.368, 0.135
[예측 2] log-log OLS 기울기 (전체 / 상위 10% / 순위 201 이후)
      멱  -1.054 / -1.135 / -1.016
     지수  -1.026 / -0.282 / -1.717
[예측 3] 1300배 전후
      멱  기울기 -1.053637 → -1.053637 (차 +0.0e+00), 절편 7.9764 → 15.1465 (차 7.1701)
     지수  기울기 -1.025636 → -1.025636 (차 +0.0e+00), 절편 8.5044 → 15.6745 (차 7.1701)
   λ̂  0.098143 → 7.5495e-05  (7.692e-04 배 = 1/1300)

3. 대조

예측과 계산이 어긋난 지점을 적는다. 어느 쪽이 틀렸는지 판정한다.

예측결과어긋남원인

본문 확인: 어긋났으면 (eq-w17-6)·(eq-w17-11)·(eq-w17-10)으로 돌아간다.