
새로운 X데이터로 통계 분석을 해보자
전처리 하기
import pandas as pd
import numpy as np
from sklearn.preprocessing import StandardScaler
df = pd.read_excel('수정본.xlsx')
cols = ['지하수_수위_최대차', '지하수_수위_변동성',
'지하수_수온_최대차', '지하수_수온_변동성',
'지하수_EC_최대차', '지하수_EC_변동성']
df[cols] = df[cols].replace(0, np.nan)
df[cols] = df[cols].fillna(df[cols].mean())
cols2 = ['사고당일_최저기온','사고당일_최고기온','사고당일_일강우량',
'사고당일 일교차(℃)','강수량 합계(mm)','사고 전 3일간 일교차 평균(℃)']
df[cols2] = df[cols2].fillna(df[cols2].mean())
if '사고발생일자' in df.columns:
df['사고발생일자'] = pd.to_datetime(df['사고발생일자'])
df['사고_월'] = df['사고발생일자'].dt.month
df.drop('사고발생일자', axis=1, inplace=True)
df['사고_월'] = df['사고_월'].astype(str)
df = pd.get_dummies(df, columns=['사고_월'], prefix='월')
else:
print("경고: '사고발생일자' 컬럼이 데이터프레임에 없습니다. '사고_월' 생성 및 컬럼 드롭을 건너뜁니다.")
if '지층 단순화' in df.columns:
df = pd.get_dummies(df, columns=['지층 단순화'], prefix='지층')
else:
print("경고: '지층 단순화' 컬럼이 데이터프레임에 없습니다. 원-핫 인코딩을 건너뜁니다.")
cols_to_standardize = [
'지하수_수위_최대차', '지하수_수위_변동성',
'지하수_수온_최대차', '지하수_수온_변동성',
'지하수_EC_최대차', '지하수_EC_변동성',
'사고당일_최저기온', '사고당일_최고기온', '사고당일_일강우량',
'사고당일 일교차(℃)', '강수량 합계(mm)', '사고 전 3일간 일교차 평균(℃)',
'지하철_노선_거리', '시설물_전체시설물_최단거리(m)',
'시설물_복개구조물_종합_최단거리(m)', '시설물_지하차도_1종_최단거리(m)',
'시설물_지하차도_2종_최단거리(m)', '시설물_지하차도_3종_최단거리(m)',
'시설물_대형건축물_1종_최단거리(m)', '시설물_대형건축물_2종_최단거리(m)',
'시설물_다중이용건축물_2종_최단거리(m)', '시설물_지하도상가_1종_최단거리(m)',
'시설물_지하도상가_2종_최단거리(m)',
'건축_진행중_최단거리(m)', '건축_완공후_최단거리(m)',
'지하철_최단거리(m)'
]
cols_to_standardize_existing = [col for col in cols_to_standardize if col in df.columns]
scaler = StandardScaler()
df[cols_to_standardize_existing] = scaler.fit_transform(df[cols_to_standardize_existing])
df.head()
t-test 분석
import pandas as pd
import numpy as np
from scipy.stats import ttest_ind
group_sinkhole_occurred = df[df['싱크홀_발생'] == 1]
group_sinkhole_not_occurred = df[df['싱크홀_발생'] == 0]
numeric_cols_for_ttest = [col for col in df.select_dtypes(include=[np.number]).columns
if col != '싱크홀_발생' and not col.startswith('지층_')]
t_results = []
for col in numeric_cols_for_ttest:
a = group_sinkhole_occurred[col].dropna()
b = group_sinkhole_not_occurred[col].dropna()
if len(a) < 2 or len(b) < 2:
print(f"경고: '{col}' 변수는 충분한 데이터가 없어 t-test를 수행할 수 없습니다.")
continue
t_stat, p_val = ttest_ind(a, b, equal_var=False)
if pd.isna(p_val):
print(f"경고: '{col}' 변수의 t-test 결과 p-value가 NaN입니다. 이 변수는 결과에 포함되지 않습니다.")
continue
significance_star = ""
if p_val < 0.05:
significance_star = "*"
t_results.append({
'변수': col,
'싱크홀_발생_평균': f"{a.mean():.4f}",
'싱크홀_미발생_평균': f"{b.mean():.4f}",
't-통계량': f"{t_stat:.4f}",
'p-value': f"{p_val:.4f}",
'유의성': significance_star
})
t_df = pd.DataFrame(t_results)
if not t_df.empty:
t_df['p-value_numeric'] = pd.to_numeric(t_df['p-value'], errors='coerce')
t_df = t_df.sort_values(by='p-value_numeric').drop(columns='p-value_numeric')
else:
print("경고: t-test를 수행할 변수가 없거나 모든 변수에서 유효한 결과가 나오지 않아 결과 DataFrame이 비어있습니다.")
print("---")
print("### Welch's t-test 결과 (싱크홀 발생: 1 vs 0)")
print("*(유의성 컬럼의 '*'는 p < 0.05를 의미합니다.)")
if not t_df.empty:
print(t_df.to_string(index=False))
else:
print("결과를 표시할 데이터가 없습니다.")
print("\n---")
print("### 통계적으로 유의미한 변수 (p < 0.05) 목록:")
if not t_df.empty:
significant_vars = t_df[t_df['유의성'] == '*']['변수'].tolist()
if significant_vars:
for var in significant_vars:
print(f"- {var}")
else:
print("통계적으로 유의미한 변수가 없습니다 (p < 0.05 기준).")
else:
print("t-test 결과 데이터가 없어 유의미한 변수를 확인할 수 없습니다.")
변수 싱크홀_발생_평균 싱크홀_미발생_평균 t-통계량 p-value 유의성
34 노선_반경내_개수_180m 0.9278 0.0636 7.8041 0.0000 *
32 지하철역_반경내_개수_180m 0.2990 0.0136 5.1217 0.0000 *
31 지하철역_최단거리(m) 471.0958 763.0763 -6.1859 0.0000 *
30 지하철역_반경내_개수_400m 0.5773 0.1500 5.4793 0.0000 *
7 시설물_전체시설물_최단거리(m) -0.3398 0.1498 -4.2044 0.0000 *
19 시설물_지하도상가_2종_최단거리(m) -0.3437 0.1516 -4.2672 0.0000 *
33 노선_거리(m) 349.3234 784.2394 -7.2346 0.0000 *
15 시설물_대형건축물_2종_최단거리(m) -0.3612 0.1593 -4.5736 0.0000 *
13 시설물_대형건축물_1종_최단거리(m) -0.3943 0.1738 -4.9033 0.0000 *
22 건축_완공후_개수(180m) 1.0515 1.9409 -4.0671 0.0001 *
11 시설물_지하차도_3종_최단거리(m) -0.2998 0.1322 -3.9999 0.0001 *
6 시설물_전체시설물_반경내_개수_170m 1.1753 0.4091 3.8858 0.0002 *
26 사고당일_일강우량 0.3781 -0.1667 3.6137 0.0004 *
28 강수량 합계(mm) 0.3246 -0.1431 3.5790 0.0005 *
17 시설물_다중이용건축물_2종_최단거리(m) -0.2649 0.1168 -3.1625 0.0018 *
10 시설물_지하차도_2종_최단거리(m) -0.2598 0.1146 -3.1487 0.0019 *
18 시설물_지하도상가_1종_최단거리(m) -0.2581 0.1138 -3.1377 0.0020 *
12 시설물_대형건축물_1종_반경내_개수_170m 0.2887 0.0136 3.1610 0.0021 *
23 건축_완공후_최단거리(m) 0.2773 -0.1222 3.0637 0.0026 *
14 시설물_대형건축물_2종_반경내_개수_170m 0.3814 0.0409 3.0388 0.0030 *
25 사고당일_최고기온 0.2031 -0.0895 2.5425 0.0117 *
16 시설물_다중이용건축물_2종_반경내_개수_170m 0.1959 0.0500 2.5507 0.0120 *
9 시설물_지하차도_1종_최단거리(m) -0.1704 0.0751 -2.4081 0.0167 *
24 사고당일_최저기온 0.1839 -0.0811 2.1732 0.0311 *
4 지하수_EC_최대차 -0.1017 0.0448 -1.4769 0.1407
5 지하수_EC_변동성 -0.0901 0.0397 -1.2948 0.1964
8 시설물_복개구조물_종합_최단거리(m) 0.1110 -0.0489 1.2491 0.2134
29 사고 전 3일간 일교차 평균(℃) 0.0817 -0.0360 1.1452 0.2531
27 사고당일 일교차(℃) 0.0597 -0.0263 0.8068 0.4205
0 지하수_수위_최대차 -0.0415 0.0183 -0.5828 0.5605
1 지하수_수위_변동성 -0.0396 0.0175 -0.5730 0.5671
20 건축_진행중_개수(180m) 0.0619 0.0818 -0.5565 0.5785
21 건축_진행중_최단거리(m) 0.0238 -0.0105 0.3207 0.7487
3 지하수_수온_변동성 0.0190 -0.0084 0.2262 0.8213
2 지하수_수온_최대차 0.0023 -0.0010 0.0285 0.9773
범주형
월
import pandas as pd
import numpy as np
from scipy.stats import chi2_contingency
df = pd.read_excel('수정본.xlsx')
df['싱크홀_발생'] = df['싱크홀_발생'].astype(int)
df['사고발생일자'] = pd.to_datetime(df['사고발생일자'])
df['사고_월'] = df['사고발생일자'].dt.month
cont_table_geo = pd.crosstab(df['지층 단순화'], df['싱크홀_발생'])
chi2_geo, p_geo, dof_geo, exp_geo = chi2_contingency(cont_table_geo, correction=False)
print("=== 지층 단순화 vs 싱크홀 발생 ===")
print(f"Chi2 = {chi2_geo:.4f}, p-value = {p_geo:.4f} {'*' if p_geo<0.05 else ''}, dof = {dof_geo}\n")
print("관측빈도:\n", cont_table_geo, "\n")
print("기대빈도:\n", pd.DataFrame(exp_geo, index=cont_table_geo.index, columns=cont_table_geo.columns), "\n")
print(("지층 단순화와 싱크홀 발생은 "
+ ("유의미한 연관성이 있습니다." if p_geo<0.05 else "연관성이 있다고 보기 어렵습니다.")))
print("\n" + "-"*50 + "\n")
cont_table_month = pd.crosstab(df['사고_월'], df['싱크홀_발생'])
chi2_mon, p_mon, dof_mon, exp_mon = chi2_contingency(cont_table_month, correction=False)
print("=== 사고_월 vs 싱크홀 발생 ===")
print(f"Chi2 = {chi2_mon:.4f}, p-value = {p_mon:.4f} {'*' if p_mon<0.05 else ''}, dof = {dof_mon}\n")
print("관측빈도:\n", cont_table_month, "\n")
print("기대빈도:\n", pd.DataFrame(exp_mon, index=cont_table_month.index, columns=cont_table_month.columns), "\n")
print(("사고_월과 싱크홀 발생은 "
+ ("유의미한 연관성이 있습니다." if p_mon<0.05 else "연관성이 있다고 보기 어렵습니다.")))
=== 지층 단순화 vs 싱크홀 발생 ===
Chi2 = 23.8663, p-value = 0.0001 *, dof = 4
관측빈도:
싱크홀_발생 0 1
지층 단순화
운모편암 9 0
충적층 43 33
편마암 97 37
홍적층 0 5
화강암 71 22
기대빈도:
싱크홀_발생 0 1
지층 단순화
운모편암 6.246057 2.753943
충적층 52.744479 23.255521
편마암 92.996845 41.003155
홍적층 3.470032 1.529968
화강암 64.542587 28.457413
지층 단순화와 싱크홀 발생은 유의미한 연관성이 있습니다.
--------------------------------------------------
=== 사고_월 vs 싱크홀 발생 ===
Chi2 = 20.1892, p-value = 0.0428 *, dof = 11
관측빈도:
싱크홀_발생 0 1
사고_월
1 23 10
2 20 4
3 14 12
4 25 8
5 12 5
6 16 9
7 30 12
8 19 20
9 11 7
10 21 4
11 13 3
12 16 3
기대빈도:
싱크홀_발생 0 1
사고_월
1 22.902208 10.097792
2 16.656151 7.343849
3 18.044164 7.955836
4 22.902208 10.097792
5 11.798107 5.201893
6 17.350158 7.649842
7 29.148265 12.851735
8 27.066246 11.933754
9 12.492114 5.507886
10 17.350158 7.649842
11 11.104101 4.895899
12 13.186120 5.813880
사고_월과 싱크홀 발생은 유의미한 연관성이 있습니다.
지층 심층 분석
import pandas as pd
import numpy as np
from sklearn.preprocessing import StandardScaler
from scipy.stats import chi2_contingency
df = pd.read_excel('수정본.xlsx')
cols = ['지하수_수위_최대차', '지하수_수위_변동성',
'지하수_수온_최대차', '지하수_수온_변동성',
'지하수_EC_최대차', '지하수_EC_변동성']
df[cols] = df[cols].replace(0, np.nan)
df[cols] = df[cols].fillna(df[cols].mean())
cols2 = ['사고당일_최저기온','사고당일_최고기온','사고당일_일강우량','사고당일 일교차(℃)','강수량 합계(mm)','사고 전 3일간 일교차 평균(℃)']
df[cols2] = df[cols2].fillna(df[cols2].mean())
df['사고발생일자'] = pd.to_datetime(df['사고발생일자'])
df['사고_월'] = df['사고발생일자'].dt.month
df.drop('사고발생일자', axis=1, inplace=True)
df = pd.get_dummies(df, columns=['지층 단순화'], prefix='지층')
numeric_cols_before_drop = df.select_dtypes(include=[np.number]).columns.tolist()
if '사고발생위치' in numeric_cols_before_drop:
numeric_cols_before_drop.remove('사고발생위치')
scaler = StandardScaler()사고_월과 싱크홀 발생은 유의미한 연관성이 있습니다.
df[numeric_cols_before_drop] = scaler.fit_transform(df[numeric_cols_before_drop])
df['싱크홀_발생'] = df['싱크홀_발생'].astype(int)
geological_layer_cols = [col for col in df.columns if col.startswith('지층_')]
chi2_results = []
for col in geological_layer_cols:
cont_table = pd.crosstab(df['싱크홀_발생'], df[col])
chi2, p, dof, exp = chi2_contingency(cont_table, correction=False)
significance_star = ""
if p < 0.05:
significance_star = "*"
chi2_results.append({
'변수': col,
'카이제곱 통계량': f"{chi2:.4f}",
'자유도': dof,
'p-value': f"{p:.4f}",
'유의성': significance_star
})
chi2_df = pd.DataFrame(chi2_results)
chi2_df['p-value_numeric'] = pd.to_numeric(chi2_df['p-value'])
chi2_df = chi2_df.sort_values(by='p-value_numeric').drop(columns='p-value_numeric')
print("---")
print("### 카이제곱 독립성 검정 결과 (지층 vs 싱크홀 발생)")
print("*(유의성 컬럼의 '*'는 p < 0.05를 의미합니다.)")
print(chi2_df.to_string(index=False))
print("\n---")
print("### 통계적으로 유의미한 지층 변수 (p < 0.05) 목록:")
significant_geological_vars = chi2_df[chi2_df['유의성'] == '*']['변수'].tolist()
if significant_geological_vars:
for var in significant_geological_vars:
print(f"- {var.replace('지층_', '')}")
else:
print("싱크홀 발생과 통계적으로 유의미한 연관성을 보이는 지층 변수가 없습니다 (p < 0.05 기준).")
---
### 카이제곱 독립성 검정 결과 (지층 vs 싱크홀 발생)
*(유의성 컬럼의 '*'는 p < 0.05를 의미합니다.)
변수 카이제곱 통계량 자유도 p-value 유의성
지층_홍적층 11.5219 1 0.0007 *
지층_충적층 7.7387 1 0.0054 *
지층_운모편암 4.0841 1 0.0433 *
지층_화강암 2.9879 1 0.0839
지층_편마암 0.9755 1 0.3233
---
### 통계적으로 유의미한 지층 변수 (p < 0.05) 목록:
- 홍적층
- 충적층
- 운모편암
?