과제 형식에 대한 피드백
- 이번 과제는 2023년 서울시 미세먼지 보고서를 코딩을 통해 데이터를 분석하는 것이다.
- 따라서 제목, 소제목, 부연 설명을 더 신경써서 보고서 형식으로 작성해야한다.
과제 내용에 대한 피드백

- 과제 제목
- 제출일/ 제출자
- 내가 해야 할 과제에 대한 세부사항
- 목표
피드백
- 추가 설명을 통해 다양한 방법으로 데이터를 이용하는 방법에 대해서 설명해 주신다.
데이터 불러오기
-
데이터를 불러올때에는 데이터 출처에 대해서 링크를 해두는 것이 좋다.
- 데이터 출처 쓰는 법
- [‘서울열린데이터광장, 서울시 기간별 일평균 대기환경 정보’]’(‘https://data.seoul.go.kr/dataList/OA-2220/S/1/datasetView.do’)’ 이런식으로 쓰면 된다.
- 데이터 출처 쓰는 법
-
index_col은 n번째 column을 index로 설정하는 파라미터이다.
-
데이터의 타입을 사전에 미리 확인하기 위해 index 설정은 나중에 하는 것을 추천
dust_reviews = pd.read_csv("C:/Users/pps/Desktop/배정환(인턴)/기간별_일평균_대기환경_정보_2023년.csv", encoding="CP949")1. 피드백
- 데이터를 불러올 때 주의해야 할 점:
encoding, 한글이 들어간 경우 깨지는 경우가 있으니 이를 해결할 수 있어야 한다. - 원활한 데이터 분석을 위해 변수를 변경할 수 있어야한다.
- Ex) 인덱싱할 때 변수를 쉽게 불러오기 위해 column명을 단순화 하면 다음과 같이 할 수 있다. 특히 시각화 라이브러리는 한글 글꼴이 지원되지 않는 경우가 많기 때문에 되도록 영어로 수정하는 것이 좋다.
dust_review.columns = ["region","station", "NO2","O3","CO","SO2","PM10","PM2.5"]데이터 정보 확인하기
dtypes
- 데이터 타입은 크게 number(
int,float),datetime,object,category로 나눌 수 있다.
dust_reviews.dtypesinfo()
- 데이터의 컬럼 명, 데이터 타입, 전체 데이터 수 등의 정보를 한 번에 이해할 수 있다.
- 즉,
df.columns,df.shape,df.dtypes를 따로 하지 않아도info를 통해 알 수 있는 것이다. dust_reviews.info()를 통해 데이터의 수는 총 9125개이며, Non-Null Count를 보면 측정값들에 결측치가 존재한다는 것을 유추할 수 있다.
dust_reviews.info()Datatype 변경
column의datetype을 변경하는 코드- 위의 코드를 통해 측정일시의
datetype이 정수인 것을 확인할 수 있다. datetype를datetime으로 변경하기 위해서는df.astype()으로string으로 바꾼 후pd.to_datetime()을 사용한다.- to_datetime에 관한 자료
dust_reviews["측정일시"] = dust_reviews["측정일시"].astype("str")
pd.to_datetime(dust_reviews["측정일시"], format="%Y$m%d")
dust_reviews.info()set_index()
- 인덱스를 설정하는 코드
- 인덱스를 다시 설정하기 위해서는
df.set_index()를 사용하면 된다. inplace파라미터는 기존의 데이터프레임에 덮어쓴다는 의미이다.
dust_reviews.set_index("측정일시", inplace=True)unique()
- column의 형식이 카테고리인 경우 카테고리 목록의 수를 확인하기 위한 함수이다.
unique()를 통해 종류를 확인한 후 그 길이를 구해도 된다.- 따라서 연속형 변수인 경우에는
unique를 해도 의미가 없다.
describe()
describe의 default는datatype이number(int, float), datetime일 때의 요약을 계산하는 함수이다.- 만일 내가 특정한
datatype에 대한 변수들만 출력하고 싶을 때에는include파라미터를 이용한다.
head(), tail()
- 처음 n개의 데이터를 확인하기 위해서는
head(n)을 사용한다. (default, n=5) - 맨 끝의 n개 데이터를 확인하기 위해서는
tail(n)을 사용한다. (default, n=5) - 시계열 데이터의 경우에는
head,tail을 모두 확인해보는 경우가 많다. (측정기간을 확인하기 위함) 시계열데이터란?
value_counts()
value_counts()는 특정column이 카테고리일 때 빈도표를 구하기 위한 함수이다.dust_reviews.value_count(subset="측정소명")도 동일한 코드이다.
# 같은 코드
dust_reviews['측정소명'].value_counts()
dust_reviews.value_count(subset="측정소명")- 멀티 인덱스를 표현할 수도 있다.
dust_reviews.value_counts(subset=["권역별","측정소명"]) - 위의 코드 결과를 보면, 모든 측정소가 동일하게 1년 동안의 모든 날짜에 대한 데이터 행이 존재함을 알 수 있다.
2. 피드백
- 데이터 정보 확인이 중요한 이유?
- 데이터 분석을 하기 전 데이터 전처리를 해야하는 데 어떤 부분을 변경해야 하는지 한번에 확인할 수 있다. 데이터 전처리란?
- 필요없는 column제거, datatype 변경 등의 처리를 할 수 있어야 한다.
- 그 중 가장 중요하게 봐야할 요소는 데이터 타입, 결측치 유무이다.
결측치 파악하기
각 column마다 결측값의 개수를 파악하기 위한 코드
- 이 데이터를 봤을 때 전체 결측치 개수는 중요하지 않다. 중요한 것은 어떤 관측소에서 결측치가 많이 발생했는가이다.
- 그룹별로 하기 위해서는
df.groupby("변수명")을 이용한다. axis=0은 열의 요소들의 합,axis=1행의 요소들의 합
dust_reviews.isnull().sum(axis=0)그룹별로 결측치 개수를 구하기 위한 코드
df.groupby("변수명").agg(lambda x: x에 대한 함수). 이때 x는 column을 의미한다.- 강동구, 중구에서 결측치가 많이 발생한 것으로 보인다.
- 왜 결측치가 많이 발생했을까? 추세를 그려 결측치가 발생한 곳을 확인해보면 알 수 있다.
dust_reviews.groupby("측정소명").agg(lambda x: x.isnull().sum())
agg메서드는 사용자에게 다중집계작업을 간결하게 나타낼수 있도록 한다.- 한번의 메서드에 여러 함수를 동시에 입력하고 결과를 출력할 수 있다.
- func : 이 입력변수는 pandas 객체의 열이나 행에 적용할 함수를 입력받는다.
groupby: 데이터를 그룹화df.grppby("그룹화할 열의 이름")df데이터프레임을그룹화할 열의 이름으로 그룹화한다.
fillna()
df.fillna()는 결측값을 대체하는 함수이다.- 아래의 코드는 ‘아황산가스농도에 대한 결측치가 있는 곳을 “Unknown”으로 넣어라’라는 의미이다.
- 그래서 아래 output을 보면 dtype이 float에서 object로 바뀐것을 볼 수 있다.
- dtype이 object가 되면 수학 연산을 할 수 없기 때문에 이를 주의해야 한다.
dust_reviews['아황산가스농도(ppm)'].fillna("Unknown")
3. 피드백
- 결측치를 파악해야하는 이유?
- 결측치를 처리하지 않으면 나중에 데이터 분석에 오류가 생길 수 있다.
- 기본적인 방법은 결측치를 제거하는 것이지만, 데이터의 특징에 따라 특정값으로 대체하기도 한다.
- 이 과제에서는 전테 데이터 분석의 흐름을 이해하기 위한 목적이기 때문에 일단 결측치 확이만 하고 처리하지 않기로 한다.
4. 측정소별 통계 파악하기
측정소별 평균 계산하는 코드
- 위의 측정소별 결측값 개수 구하는 코드를 활용한다.
sub_data = dust_reviews.drop(columns="권역명").groupby("측정소명").agg(func="mean")
측정소별 통계 계산하는 코드
sub_data = dust_reviews.drop(columns="권역명").groupby("측정소명").agg(func=["min","mean","max"])
2023년 초미세먼지 평균이 높은 순으로 정리하기
- 2023년 연평균 초미세먼지는 노원구가 가장 높았으며, 서대문구가 가장 낮았다 → 결과 도출
by: 정렬 기준이 될 레이블
sub_data["초미세먼지(㎍/㎥)"].sort_values(by="mean", ascending=False)
4. 피드백
- 이 과제의 목적은 “2023년의 미세먼지가 어땠는가”에 대해 정리하는 것이다.
- 각 함수들을 써서 데이터의 어떤 특징을 설명하고 싶은지를 잘 알고 있어야 한다.
- 예를 들어 각 관측소명마다의 평균을 구하는 이유는
- 관측소들마다 미세먼지의 큰 차이가 있었는지 확인해보고 싶었기 때문이다.
5. 시각화
2023년 연평균 초미세먼지 농도가 가장 높았던 노원구의 모든 지표들에 대한 Boxplot 그리는 코드
# 문제1과 문제2 가 발생된 코드
sub_data = dust_reviews[dust_reviews["측정소명"] == "노원구"].drop(columns=["권역명","측정소명"])
sub_data.boxplot()문제 1. 미세먼지의 값이 다른 지표들에 비해 높다보니 boxplot이 제대로 보이지 않음 - 따로 그리기 문제 2. 라벨이 겹치는 문제 발생 - 그림 크기 변경
- 위의 문제를 해결하는 코드
- 한번에 그림 여러개 그리기
fig: 데이터가 담기는 프레임. 여러 그래프가 담길 수 있는 액자 같은 역할 / 그림ax: 실제 데이터가 그려지는 캔버스. 모든 plot은 이 axes 위에서 이루어져야 함 /그림에 그려질 그래프- ex) 하나의 그림에 그래프를 2행 3열로 그린다.
fix, ax = plt.subpolts(nrows=2 ncols=3)
-
figsize(가로길이, 세로길이): 그래프의 사이즈를 설정 (단위는 inch) -
문제 해결 코드
fig, ax = plt.subplots(nrows=1, ncols=6, figsize=(25,5))
columns = sub_data.columns
for i in range(sub_data.shape[1]):
sub_data.boxplot(column=columns[i],ax=ax[i])sub_data.shape[1]: 열 개수sub_data.shape[0]: 행 개수sub+data.boxplot(column=columns[i], ax=ax[i]- :
columns[i]열에 대한 박스 플롯을 생성 - :
ax배열의 i번째 서브플롯에 할당
- :
측정소별로 2023년 연평균 초미세먼지 농도 boxplot 그리기
pivot_table은 데이터프레임을 재구성할 때 편리한 함수이다.- 이 그래프는 측정소별 지표값을 한눈에 비교할 수 있다.
sub_data = dust_reviews.pivot_table(index = "측정일시", columns="측정소명", values="초미세먼지(㎍/㎥)")
plt.figure(figsize=(20,5))
sub_data.boxplot()
sub_data = dust_reviews.pivot_table(index = "측정일시",columns="측정소명",values="초미세먼지(㎍/㎥)") 의 DataFrame

plt.figure(): Matplotlib에서 그림(figure) 객체를 생성하기 위해 사용됩니다.- 반드시 figure을 사용해야하는 경우
- 복수 개의 그림을 동시에 그리고 싶을 때
- 그림의 크기, 해상도, 배경색 등의 속성을 직접 지정하고 싶을 때
- 반드시 figure을 사용해야하는 경우
df.pivot_tabe(): 데이터프레임을 피벗 테이블로 변환하는 데 사용됨- 피벗 테이블은 데이터프레임의 열을 재구성하여 데이터를 요약하고 분석하는데 유용한 형식으로 변환됨.
- 매개변수
values: 피벗 테이블에 나타낼 값 열(들)index: 피벗 테이블의 행 인덱스로 사용할 열(들)columns: 피벗 테이블의 열 인덱스로 사용할 열(들)aggfumc: 집계 함수 (기본값은 ‘mean’)로 데이터를 집계할 방법을 지정
권역별로 2023년 연평균 초미세먼지 농도 boxplot 그리기
sub_data = dust_reviews.pivot_table(index="측정일시", columns="권역명", values="초미세먼지(㎍/㎥)")
plt.figure(figsize=(20,5))
sub_data.boxplot()
광진구의 모든 측정값들의 추세를 나타낸다.
# 문제1 과 문제2가 발생한 코드
import matplotlib.pyplot as plt
dust_reviews.loc[dust_reviews["측정소명"]=="광진구"].drop(columns=["권역명","측정소명"]).plot()- 결과값

문제1. 한글 깨짐 문제 문제2. x축이 이상하다. - 데이터는 측정 일자였지만, 현재 이 데이터프레임은 이를 숫자로 인식하고 있다. - 끊어진듯한 선들이 있는데 이 이유는 20230131 → 20230201 의 차이를 숫자로 인식했기 때문이다.
- 문제 해결 코드
# 아래 코드처럼 측정일시의 datatype을 datetime으로 변경했다.
sub_data = dust_reviews_origin.loc[dust_reviews_origin["측정소명"] == "광진구"].drop(columns=["권역명","측정소명"])
sub_data.plot()# datatype을 datetime으로 변경하기 위해서는 df.astype()으로 string으로 바꾼 후 pd.to_datetime()을 사용한다.
dust_reviews_origin["측정일시"] = dust_review_origin["측정일시"].astype("str")
review = pd.to_datetime(dust_reviews_origin["측정일시"], format="%Y%m%d")
한번에 그림 여러 개 그리기
- 각 지표마다 추세가 다른 것을 확인할 수 있다. 이를 설명할 줄 알아야 한다.
fig, ax = plt.subplots(1,6, figsize=(25,5))
columns = sub_data.columns
for i in range(sub_data.shape[1]):
sub_data.iloc[:,i].plot(ax=ax[i])
ax[i].set_title(columns[i])sub_data.iloc[:, i].plot(ax=ax[i]):sub_data.iloc[:,i]는 데이터프레임의 i번째 열을 선택.plot(ax=ax[i])는 선택된 열의 데이터를ax배열의 i번째 서브플롯에 그립니다.
ax[i].set_title(columns[i]):ax[i]는 i번째 서브플롯을 나타냅니다..set_title(columns[i])는 해당 서브플롯의 제못을columns[i]로 설정columns[i]는 i번째 열의 이름입니다.

5. 피드백
- 시각화를 하기 위해서는 어떤 데이터가 필요한지에 대해 잘 알고 있어야 한다.
- 각 시각화를 통해 추출할 수 있는 정보가 무엇인지 알고 있어야 한다.
- 데이터를 보고 어떤 그래프를 그려야 할 지 파악할 수 있어야 한다.
- ex) 연속형 변수-선 그래프, 카테고리 변수-막대 그래프 등
- 시각화는 정답이 없다. 계속 그려보면서 코드는 정확하게 기억하지 못해도 어떻게 그려야할지는 빠르게 파악할 수 있어야 한다.