1주차 과제

과제 형식에 대한 피드백

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

과제 내용에 대한 피드백

  • 과제 제목
  • 제출일/ 제출자
  • 내가 해야 할 과제에 대한 세부사항
  • 목표

피드백

  • 추가 설명을 통해 다양한 방법으로 데이터를 이용하는 방법에 대해서 설명해 주신다.

데이터 불러오기

  • 데이터를 불러올때에는 데이터 출처에 대해서 링크를 해두는 것이 좋다.

  • 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.dtypes

info()

  • 데이터의 컬럼 명, 데이터 타입, 전체 데이터 수 등의 정보를 한 번에 이해할 수 있다.
  • 즉, df.columns, df.shape, df.dtypes를 따로 하지 않아도 info를 통해 알 수 있는 것이다.
  • dust_reviews.info()를 통해 데이터의 수는 총 9125개이며, Non-Null Count를 보면 측정값들에 결측치가 존재한다는 것을 유추할 수 있다.
dust_reviews.info()

Datatype 변경

  • columndatetype을 변경하는 코드
  • 위의 코드를 통해 측정일시의 datetype이 정수인 것을 확인할 수 있다.
  • datetypedatetime으로 변경하기 위해서는 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는 datatypenumber(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을 사용해야하는 경우
      • 복수 개의 그림을 동시에 그리고 싶을 때
      • 그림의 크기, 해상도, 배경색 등의 속성을 직접 지정하고 싶을 때
  • 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) 연속형 변수-선 그래프, 카테고리 변수-막대 그래프 등
  • 시각화는 정답이 없다. 계속 그려보면서 코드는 정확하게 기억하지 못해도 어떻게 그려야할지는 빠르게 파악할 수 있어야 한다.