Random Generation Fitting to a Histogram

  • 새로운 데이터는 numpy.random함수로 만들 수 있습니다.
  • 정규분포나 균일하게 만드는 것은 많이들 합니다만,
  • 기존 데이터의 분포를 모방해 봅시다.

RIP tutorial: Fitting a function to data from a histogram

1. 기존 데이터

1
2
3
import pandas as pd
df = pd.read_pickle("randgen_dist.pkl")
display(df.describe())

seaborn.set_palette
seaborn.set_context
seaborn.set_style
seaborn.histplot

  • 분포를 확인해보겠습니다.
  • 보고용 그림이 아니므로 손을 많이 대고 싶지 않습니다.
  • 스타일을 잡고 간단히 그립니다.
  • seaborn이 이럴 때 좋습니다.
1
2
3
4
5
6
7
8
9
10
11
import matplotlib.pyplot as plt
import seaborn as sns
sns.set_palette("muted")
sns.set_context("talk")
sns.set_style("white")

fig, axs = plt.subplots(ncols=3, figsize=(12, 4))
sns.histplot(df["A"], ax=axs[0], kde=True, bins=100)
sns.histplot(df["B"], ax=axs[1], kde=True, bins=100)
sns.histplot(df["C"], ax=axs[2], kde=True, bins=100, kde_kws={"bw_adjust":5})
fig.tight_layout()

seaborn.kdeplot
seaborn.regplot
pandas.DataFrame.corr

  • 2변수 분포를 확인해보겠습니다.
  • 2차원 KDE plot을 그리고 pearson correlation coefficient를 봅니다.
1
2
3
4
PCC_AB = df.corr().loc["A", "B"]
PCC_BC = df.corr().loc["B", "C"]
PCC_AC = df.corr().loc["A", "C"]
print(PCC_AB, PCC_BC, PCC_AC)

실행결과: 피어슨 계수가 모두 작네요.

1
0.2813347529044316 0.025736569810035914 0.0011956781154378422
  • 2차원 분포에 같이 얹어봅니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
fig, axs = plt.subplots(ncols=3, figsize=(12, 4))
sns.kdeplot(x=df["A"], y=df["B"], cmap="Blues", ax=axs[0])
sns.regplot(x=df["A"], y=df["B"], scatter=False, ax=axs[0])
axs[0].annotate(f"PCC = {PCC_AB:0.2f}", (0.5, 0.8), xycoords='axes fraction', ha="center")

sns.kdeplot(x=df["B"], y=df["C"], cmap="Blues", ax=axs[1])
sns.regplot(x=df["B"], y=df["C"], scatter=False, ax=axs[1])
axs[1].annotate(f"PCC = {PCC_BC:0.2f}", (0.5, 0.8), xycoords='axes fraction', ha="center")

sns.kdeplot(x=df["A"], y=df["C"], cmap="Blues", ax=axs[2])
sns.regplot(x=df["A"], y=df["C"], scatter=False, ax=axs[2])
axs[2].annotate(f"PCC = {PCC_AC:0.2f}", (0.5, 0.8), xycoords='axes fraction', ha="center")

fig.tight_layout()

  • A와 B 사이에 상관관계가 있지만 매우 약합니다.
  • C는 A, B와 전혀 관계가 없어보입니다.

2. histogram fitting

  • 원 데이터의 분포를 재현하려면 함수로 모사해야 합니다.
  • 히스토그램이 정규분포만으로는 설명이 되지 않습니다.
  • AB : $$y = (ax + b) + e^{-\frac{1}{2} (\frac{x-\mu}{\sigma})^2}$$로,
  • C : $$y = (ax + b) + Ae^{-\frac{1}{2} (\frac{x-\mu_A}{\sigma_A})^2} + Be^{-\frac{1}{2} (\frac{x-\mu_B}{\sigma_B})^2}$$로 가정합니다.
1
2
3
4
5
6
7
8
9
10
11
12
from scipy.optimize import curve_fit

def fit_lingau(x, a, b, B, mu, sigma):
func_lin = a * x + b
func_gaussian = B * np.exp(-1.0 * (x - mu)**2 / (2 * sigma**2))
return func_lin + func_gaussian

def fit_lingaugau(x, a, b, A, muA, sigmaA, B, muB, sigmaB):
func_lin = a * x + b
func_gaussian1 = A * np.exp(-1.0 * (x - muA)**2 / (2 * sigmaA**2))
func_gaussian2 = B * np.exp(-1.0 * (x - muB)**2 / (2 * sigmaB**2))
return func_lin + func_gaussian1 + func_gaussian2
  • 데이터 A의 히스토그램 계급과 갯수를 추출합니다.
1
2
3
4
5
6
7
8
9
10
11
# histogram of A
counts_A, bins_A = np.histogram(df["A"], bins=100)
bins_Ac = np.array([bins_A[i]*0.5 + bins_A[i+1]*0.5 for i in range(len(counts_A))])

# fit for histogram of A
popt_A, pcov_A = curve_fit(fit_lingau, xdata=bins_Ac, ydata=counts_A,
p0=[-20.0, 500.0, 3000.0, 10.0, 3.0])

# new x range
xa_new = np.linspace(df["A"].min(), df["A"].max(), 100)
ya_new = fit_lingau(xa_new, *popt_A)
  • 데이터 B와 C도 같은 요령으로 처리합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
fig, axs = plt.subplots(ncols=3, figsize=(12, 4))

# A
sns.histplot(df["A"], ax=axs[0], kde=True, bins=100)
axs[0].plot(xa_new, ya_new, c="r")
# B
sns.histplot(df["B"], ax=axs[1], kde=True, bins=100)
axs[1].plot(xb_new, yb_new, c="r")
axs[1].set_ylabel("")
axs[1].set_ylim(0,)

# C
sns.histplot(df["C"], ax=axs[2], kde=True, bins=100, kde_kws={"bw_adjust":5}, label="data")
axs[2].plot(xc_new, yc_new, c="r", label="fit")
axs[2].set_ylabel("")
axs[2].set_ylim(0,)

axs[2].legend(bbox_to_anchor=(1, 1))
fig.tight_layout()

  • fitting된 함수가 데이터 분포와 잘 맞습니다.
  • C는 모양을 조금 더 잘 반영하고자 gaussian을 하나 더 추가해서 의도적으로 조금 복잡하게 만들었습니다.

3. 데이터 생성

numpy.random.choice

  • numpy.randomchoice명령으로 샘플링을 할 수 있습니다.
1
2
A = [1, 2, 3]
np.random.choice(A, 10)

실행결과: 균일하게 뽑혀나옵니다.

1
array([2, 1, 1, 3, 2, 2, 1, 2, 3, 2])

  • 추출 확률을 조정할 수 있습니다.
  • 1, 2, 3 중 3만 뽑히도록 조작합시다.
1
2
3
A = [1, 2, 3]
p = [0, 0, 1]
np.random.choice(A, 10, p=p)

실행결과: p=1로 설정된 3만 반복해서 나옵니다.

1
array([3, 3, 3, 3, 3, 3, 3, 3, 3, 3])
  • 확률을 조정해 추출하는 함수를 만듭니다.

    • 분포를 재현할 함수가 필요합니다.
    • fitting 함수 이름과 파라미터를 입력받도록 합니다.
    • 함수에 맞춰 지정된 갯수의 데이터를 생성합니다.
  • 데이터 생성 및 추출 함수입니다.

1
2
3
4
5
def gen_data(x, fit_func, fit_param, number):
prob_ = fit_func(x, *fit_param)
prob_ = np.array([p if p>0 else 0 for p in prob_])
prob = prob_/prob_.sum()
return np.random.choice(x, number, p=prob)
  • 데이터를 10000개 생성합니다.
1
2
3
gen_A = gen_data(xa_new, fit_lingau, popt_A, 10000)
gen_B = gen_data(xb_new, fit_lingau, popt_B, 10000)
gen_C = gen_data(xc_new, fit_lingaugau, popt_C, 10000)
  • 생성된 데이터의 분포를 원본과 비교합니다.
  • 원본의 빨간 선생성데이터의 녹색거의 일치합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
fig, axs = plt.subplots(ncols=3, figsize=(12, 4))

# A
sns.histplot(gen_A, bins=100, ax=axs[0], color="green", alpha=0.3)
axs[0].plot(xa_new, fit_lingau(xa_new, *popt_A)/df.shape[0]*10000, c="r")
# B
sns.histplot(gen_B, bins=100, ax=axs[1], color="green", alpha=0.3)
axs[1].plot(xb_new, fit_lingau(xb_new, *popt_B)/df.shape[0]*10000, c="r")
# C
sns.histplot(gen_C, bins=100, ax=axs[2], color="green", alpha=0.3, label="generated")
axs[2].plot(xc_new, fit_lingaugau(xc_new, *popt_C)/df.shape[0]*10000, c="r", label="fit")

xlabels=["A", "B", "C"]
for i in range(3):
axs[i].set_xlabel(xlabels[i])
axs[i].set_ylim(0,)
if i != 0:
axs[i].set_ylabel("")

axs[2].legend(bbox_to_anchor=(1, 1))
fig.tight_layout()

  • 생성된 데이터간의 상관성을 확인합시다.
1
2
3
df_gen = pd.DataFrame({"A":gen_A, "B":gen_B, "C": gen_C})
gen_corr = df_gen.corr()
gen_corr

  • 독립적으로 생성되었기 때문에 변수간 관계가 없습니다.
  • 다시 2변수 분포를 확인합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
gen_PCC_AB = df_gen.corr().loc["A", "B"]
gen_PCC_BC = df_gen.corr().loc["B", "C"]
gen_PCC_AC = df_gen.corr().loc["A", "C"]

fig, axs = plt.subplots(ncols=3, figsize=(12, 4))
sns.kdeplot(x=df_gen["A"], y=df_gen["B"], cmap="Greens", ax=axs[0])
sns.regplot(x=df_gen["A"], y=df_gen["B"], scatter=False, ax=axs[0])
axs[0].annotate(f"PCC = {gen_PCC_AB:0.2f}", (0.5, 0.8), xycoords='axes fraction', ha="center")

sns.kdeplot(x=df_gen["B"], y=df_gen["C"], cmap="Greens", ax=axs[1])
sns.regplot(x=df_gen["B"], y=df_gen["C"], scatter=False, ax=axs[1])
axs[1].annotate(f"PCC = {gen_PCC_BC:0.2f}", (0.5, 0.8), xycoords='axes fraction', ha="center")

sns.kdeplot(x=df_gen["A"], y=df_gen["C"], cmap="Greens", ax=axs[2])
sns.regplot(x=df_gen["A"], y=df_gen["C"], scatter=False, ax=axs[2])
axs[2].annotate(f"PCC = {gen_PCC_AC:0.2f}", (0.5, 0.8), xycoords='axes fraction', ha="center")

fig.tight_layout()

  • A와 B 사이의 상관관계가 소멸되었습니다.
  • 그러나 B와 C, A와 C2차원 분포까지 재현되었습니다.
  • 약한 상관성을 어떻게 확보할지는 더 고민해보겠습니다.


도움이 되셨나요? 카페인을 투입하시면 다음 포스팅으로 변환됩니다

Share