accident_stat.xlsx는 교통사고 유형별 교통사고 사고건수, 사망자수, 중상자수 등을 요약한 데이터이다.
(1) 사고 유형 대분류가 "차대차"인 사고유형별 사망비율을 막대 그래프로 시각화 하시오. 단, 사망비율은 사망자수/사고건수로 계산하며, matplotlib에서 폰트는 아래 코드를 이용하여 맑은 고딕으로 설정합니다.
from matplotlib import pyplot as plt
plt.rcParams['font.family'] = 'Malgun Gothic'
accident = pd.read_excel('./accident_stat.xlsx')
accident
처음엔,
X = accident['사고유형'][accident['사고유형대분류'] == '차대차']
y = accident['사망자수'] / accident['사고건수']
plt.bar(X,y)
plt.show()
이렇게 했더니
ValueError: shape mismatch: objects cannot be broadcast to a single shape. Mismatch is between arg 0 with shape (5,) and arg 1 with shape (17,).
이런 에러가 났다. 'X'와 'y'의 길이가 맞지 않아 발생한 문제이다.
이를 해결하기 위해서는 '사고유형'별로 '사망비율'을 구하고, 이를 막대 그래프로 시각화 해야한다.
데이터프레임 그룹화 -> 각 그룹에 대해 사망비율 계산
# 먼저, '차대차' 사고유형만 선택
#-> 이 경우에 해당하는 그래프를 그리는거니까 이렇게 필터링해서 전체에 적용해줘야함.
filtered_accident = accident[accident['사고유형대분류']=='차대차']
# 사고유형별 사망비율 계산
grouped_accident = filtered_accident.groupby('사고유형').sum()
grouped_accident
grouped_accident['사망비율'] = grouped_accident['사망자수'] / grouped_accident['사고건수']
여기서 'groupby'와 'sum'을 사용하여 각 '사고유형'별로 '사망자수'와 '사고건수'를 합산하는 이유는 동일한 '사고유형'이 여러번 나타날 수 있기 때문
-> '사고유형'별로 데이터가 여러 행으로 나뉘어 있을 경우, 이를 하나의 값으로 합산하여 계산해야 함
예를 들어,
사고유형대분류 사고유형 사망자수 사고건수
차대차 추돌 3 100
차대차 추돌 2 150
차대차 측면충돌 4 120
차대차 측면충돌 1 80
차대차 정면충돌 5 90
이런 데이터가 있을 때, 사고유형별로 합산하지 않으면 각 행에 대해 사망비율을 개별적으로 계산하게 되어 잘못된 결과가 나옴.
-> 각 '사고유형'별로 '사망자수'와 '사고건수'를 합산한 후 사망비율을 계산해야함.
(1. 각 '사고유형'별로 '사망자수'와 '사고건수'를 합산
2. 합산된 값을 사용하여 사망비율을 계산)
합산 후의 데이터는 다음과 같음.
사고유형 사망자수 사고건수
추돌 5 250
측면충돌 5 200
정면충돌 5 90
그리고 각 '사고유형'별로 사망비율을 계산하면, 다음과 같음.
사고유형 사망비율
추돌 0.02
측면충돌 0.025
정면충돌 0.0556
다시 돌아와서 결론적으로, grouped accident는
이런 형태임.
** 주의할점, 여기서 '사고유형'은 인덱스가 됨.
X 에 grouped_accident['사고유형']을 사용하면, '사고유형' 컬럼을 찾으려고 하는데, 이 컬럼은 이미 인덱스로 설정되어 있으므로 존재하지 않음.
=> grouped_accident.index를 사용해야 함.
plt.bar(grouped_accident.index, grouped_accident['사망비율'])
plt.show()
최종 코드
import pandas as pd
import numpy as np
from matplotlib import pyplot as plt
plt.rcParams['font.family'] = 'Malgun Gothic'
accident = pd.read_excel('./accident_stat.xlsx')
# 먼저, '차대차' 사고유형만 선택
#-> 이 경우에 해당하는 그래프를 그리는거니까 이렇게 필터링해서 전체에 적용해줘야함.
filtered_accident = accident[accident['사고유형대분류']=='차대차']
# 사고유형별 사망비율 계산
grouped_accident = filtered_accident.groupby('사고유형').sum()
grouped_accident['사망비율'] = grouped_accident['사망자수'] / grouped_accident['사고건수']
plt.bar(grouped_accident.index, grouped_accident['사망비율'])
plt.show()
해설)
사고유형대분류가 차대차인 데이터를 필터링하여 f_df에 저장하고, f_df에서 사망비율을 계산하여 시각화함.
df = pd.read_excel('./accident_stat.xlsx')
f_df = df.loc[df['사고유형대분류'] == '차대차'] #필터링
f_df['사망비율'] = f_df['사망자수'] / f_df['사고건수']
plt.bar(f_df['사고유형'], f_df['사망비율'])
plt.show()
range(len이랑 xticks 써서 이렇게 할 수도 있긴한데.. 굳이?
(2) 사고유형대분류별로 사망자수가 가장 많은 사고 유형을 출력하세요. 그 결과는 ['횡단중', '측면충돌', '공작물충돌'] 이어야 합니다.
이 때, 사망자수의 최댓값을 구하는게 아니라 사망자수의 최댓값을 갖는 -> 사고유형을 찾는 것이므로 idxmax를 활용해야함.
** idxmax 와 idxmin 은 각각 축에서 최대/최소 값의 인덱스를 반환하는 method
예를 들어서,
accident.groupby('사고유형대분류')['사망자수'].idxmax()
이렇게 하면, 사고유형대분류에 따른 사망자수의 최댓값을 갖는 인덱스를 반환함.
밑에처럼 말이다.
사고유형대분류
차대사람 0
차대차 6
차량단독 12
Name: 사망자수, dtype: int64
우린 이 인덱스를 가진, 사고 유형을 찾으려는 거니까 여기에 loc 을 써서, 저 인덱스에 해당하는 '사고유형' 값을 추출한다..
accident.loc[accident.groupby('사고유형대분류')['사망자수'].idxmax(),'사고유형']
0 횡단중
6 측면충돌
12 공작물충돌
Name: 사고유형, dtype: object
Q. loc은 레이블 사용하여 명시적으로 데이터 선택할 때 쓰는거고, 정수 인덱스 사용하여 위치 기반으로 데이터 선택할 때는 iloc을 써야하지 않는가?
A. 'loc'은 레이블을 사용하고, 'iloc'은 위치 기반 인덱스를 사용하여 데이터를 선택하는게 맞다. 그런데 'groupby'와 'idxmax'를 사용한 결과는 레이블 같은 인덱스를 반환하기 때문에 'loc'을 사용하는 것이 맞다.
-> idxmax가 반환하는 것은 각 그룹에서 사망자수가 가장 많은 행의 인덱스이므로 'loc'을 선택해야함.
++ .tolist() 쓰면 리스트 형태로 도출할 수 있다.
accident.loc[accident.groupby('사고유형대분류')['사망자수'].idxmax(),'사고유형'].tolist()
['횡단중', '측면충돌', '공작물충돌']
(3) 사고유형별 중상비율을 계산하고 중상비율이 50%를 넘는 데이터만 중상비율.csv로 저장합니다.
accident['중상비율'] = accident['중상자수'] / accident['사고건수']
accident.loc[accident['중상비율' > 0.5]].to_csv("중상비율.csv", index = False, encoding = "cp949")
여기서 한글이 포함되어 있어 encoding을 cp949로 설정하였다.
'데이터 분석' 카테고리의 다른 글
[데이터분석 코딩연습 #1] 미세먼지 농도 (9) | 2024.07.22 |
---|