8 minute read

주식 데이터를 활용한 EDA project (1)

EDA는 탐색적 데이터 분석으로 분석에 들어가기에 앞서 데이터 분석가가 데이터를 다양한 각도에서 관찰하고 직관적으로 이해하기 위한 데이터 분석입니다. 이번 프로젝트는 주식 뉴스 기사 데이터에 대한 EDA를 pandas를 이용하여 수행하는 프로젝트입니다. EDA 와 Pandas는 데이터 분석가의 눈과 귀이라고 해도 과언이 아니기 때문에 본 프로젝트를 통해 Pandas의 기능을 이용하여 간단하게 데이터에서의 유의미한 패턴과 분포를 찾아내고 시각화하는것에 중점을 두었습니다.

EDA는 정해진 답을 찾아내는 것은 아닙니다. 데이터의 유의미한 패턴 및 내제되어 있는 의미를 찾아내는 과정 모두가 EDA 입니다.

1. Import Library

데이터 분석을 위한 기본적인 라이브러리를 import합니다. 그리고 분석을 하려는 데이터를 살펴보겠습니다. (데이터의 위치는 쥬피터 노트북과 같은 폴더내에, datasets이라는 폴더아래에 들어있습니다.)

import numpy as np
import pandas as pd
import seaborn as sns  # Visualization을 위한 라이브러리입니다.
import matplotlib.pyplot as plt

import os
print(os.listdir("./datasets"))
['state-abbrevs.csv', 'state-areas.csv', 'state-population.csv', 'upload_DJIA_table.csv']

upload_DJIA_table.csv 데이터를 로딩하고, 데이터의 처음 5개 row를 간단하게 출력해봅니다.

df = pd.read_csv('./datasets/upload_DJIA_table.csv')
df.head()
Date Open High Low Close Volume Adj Close
0 2016-07-01 17924.240234 18002.380859 17916.910156 17949.369141 82160000 17949.369141
1 2016-06-30 17712.759766 17930.609375 17711.800781 17929.990234 133030000 17929.990234
2 2016-06-29 17456.019531 17704.509766 17456.019531 17694.679688 106380000 17694.679688
3 2016-06-28 17190.509766 17409.720703 17190.509766 17409.720703 112190000 17409.720703
4 2016-06-27 17355.210938 17355.210938 17063.080078 17140.240234 138740000 17140.240234

2. Summarize Data

Pandas의 함수를 활용하여 데이터의 기본적인 정보를 살펴봅니다. 특히 sum, mean, max, min, quantile 등은 데이터를 요약하는 measurement 입니다. 큰 데이터를 하나의 요약된 수치로 변경하여 데이터의 이해를 높이는 정보입니다.

  • 함수 1 : DataFrame의 Index, Columns, Data types, Memory usage 정보
  • 함수 2 : 행과 열의 수
  • 함수 3 : 열의 이름
  • 함수 4 : 데이터의 수 (행의 수)
  • 함수 5 : 각 컬럼별 데이터의 총 합
  • 함수 6 : 각 컬럼별 데이터의 평균
  • 함수 7 : 각 컬럼별 데이터의 표준편차
  • 함수 8 : 각 컬럼별 데이터의 1,2,3 사분위수
  • 함수 9 : 각 컬럼별 데이터의 maximum 값
  • 함수 10 : 각 컬럼의 통계치
# 함수 1 : DataFrame의 Index, Columns, Data types, Memory usage 정보
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 1989 entries, 0 to 1988
Data columns (total 7 columns):
Date         1989 non-null object
Open         1989 non-null float64
High         1989 non-null float64
Low          1989 non-null float64
Close        1989 non-null float64
Volume       1989 non-null int64
Adj Close    1989 non-null float64
dtypes: float64(5), int64(1), object(1)
memory usage: 108.9+ KB
# 함수 2 : 행과 열의 수
df.shape
(1989, 7)
# 함수 3 : 열의 이름
df.columns
Index(['Date', 'Open', 'High', 'Low', 'Close', 'Volume', 'Adj Close'], dtype='object')
# 함수 4 : 데이터의 수 (행의 수)
df.count()
Date         1989
Open         1989
High         1989
Low          1989
Close        1989
Volume       1989
Adj Close    1989
dtype: int64
# 함수 5 : 각 컬럼별 데이터의 총 합
df.sum()
Date         2016-07-012016-06-302016-06-292016-06-282016-0...
Open                                               2.67702e+07
High                                               2.69337e+07
Low                                                2.65988e+07
Close                                               2.6778e+07
Volume                                            323831020000
Adj Close                                           2.6778e+07
dtype: object

마지막 3개 데이터에 대하여 High 컬럼의 합을 구해봅니다.

df.tail(3)
Date Open High Low Close Volume Adj Close
1986 2008-08-12 11781.700195 11782.349609 11601.519531 11642.469727 173590000 11642.469727
1987 2008-08-11 11729.669922 11867.110352 11675.530273 11782.349609 183190000 11782.349609
1988 2008-08-08 11432.089844 11759.959961 11388.040039 11734.320312 212830000 11734.320312
np.sum(df.iloc[-3:,2])
35409.419922
# 함수 6 : 평균
df.mean()
Open         1.345912e+04
High         1.354130e+04
Low          1.337293e+04
Close        1.346303e+04
Volume       1.628110e+08
Adj Close    1.346303e+04
dtype: float64
# 함수 7 : 표준편차
df.std()
Open         3.143282e+03
High         3.136272e+03
Low          3.150421e+03
Close        3.144007e+03
Volume       9.392343e+07
Adj Close    3.144007e+03
dtype: float64

피드백

  • 현재와 같이 사분위수 별로 구하셔도 무방하고 아래와 같이 한번에 구할수도 있으니 참고 부탁드리겠습니다.
  • df.quantile([0.25, 0.5, 0.75])
# 함수 8 : 1 사분위수
df.quantile(0.25)
Open         1.090734e+04
High         1.100098e+04
Low          1.082476e+04
Close        1.091338e+04
Volume       1.000000e+08
Adj Close    1.091338e+04
Name: 0.25, dtype: float64
# 함수 8 : 2 사분위수
df.quantile(0.5)
Open         1.302205e+04
High         1.308811e+04
Low          1.295313e+04
Close        1.302558e+04
Volume       1.351700e+08
Adj Close    1.302558e+04
Name: 0.5, dtype: float64
# 함수 8 : 3 사분위수
df.quantile(0.75)
Open         1.647770e+04
High         1.655007e+04
Low          1.639277e+04
Close        1.647841e+04
Volume       1.926000e+08
Adj Close    1.647841e+04
Name: 0.75, dtype: float64
# 함수 9 : 최대값
df.max()
Date         2016-07-01
Open            18315.1
High            18351.4
Low             18272.6
Close           18312.4
Volume        674920000
Adj Close       18312.4
dtype: object

처음 5개의 데이터에 대하여 Low 컬럼의 maximum 값을 구해봅니다.

df.head(5)
Date Open High Low Close Volume Adj Close
0 2016-07-01 17924.240234 18002.380859 17916.910156 17949.369141 82160000 17949.369141
1 2016-06-30 17712.759766 17930.609375 17711.800781 17929.990234 133030000 17929.990234
2 2016-06-29 17456.019531 17704.509766 17456.019531 17694.679688 106380000 17694.679688
3 2016-06-28 17190.509766 17409.720703 17190.509766 17409.720703 112190000 17409.720703
4 2016-06-27 17355.210938 17355.210938 17063.080078 17140.240234 138740000 17140.240234
np.max(df.iloc[:5,3])
17916.910156
# 함수 10 : 각 컬럼의 통계치
df.describe()
Open High Low Close Volume Adj Close
count 1989.000000 1989.000000 1989.000000 1989.000000 1.989000e+03 1989.000000
mean 13459.116048 13541.303173 13372.931728 13463.032255 1.628110e+08 13463.032255
std 3143.281634 3136.271725 3150.420934 3144.006996 9.392343e+07 3144.006996
min 6547.009766 6709.609863 6469.950195 6547.049805 8.410000e+06 6547.049805
25% 10907.339844 11000.980469 10824.759766 10913.379883 1.000000e+08 10913.379883
50% 13022.049805 13088.110352 12953.129883 13025.580078 1.351700e+08 13025.580078
75% 16477.699219 16550.070312 16392.769531 16478.410156 1.926000e+08 16478.410156
max 18315.060547 18351.359375 18272.560547 18312.390625 6.749200e+08 18312.390625

3. 상관관계

데이터의 상관관계를 분석하는 것은 중요한 분석과정입니다. 특성간의(feature) 상관관계를 분석하여 특성의 선형성을 발견할 수 있습니다. 또한 상관관계는 feature를 선택할 때 사용하는 중요한 지표중에 하나입니다. 상관관계를 테이블로 표현하는 방법과 시각화하여 표현하는 방법을 모두 살펴보겠습니다.

# (1) 상관관계를 DataFrame으로 표현하기
df.corr()
Open High Low Close Volume Adj Close
Open 1.000000 0.999592 0.999436 0.998991 -0.691621 0.998991
High 0.999592 1.000000 0.999373 0.999546 -0.686997 0.999546
Low 0.999436 0.999373 1.000000 0.999595 -0.699572 0.999595
Close 0.998991 0.999546 0.999595 1.000000 -0.694281 1.000000
Volume -0.691621 -0.686997 -0.699572 -0.694281 1.000000 -0.694281
Adj Close 0.998991 0.999546 0.999595 1.000000 -0.694281 1.000000
# (2). 상관관계를 DataFrame으로 표현하기
plt.imshow(df.corr(), cmap='viridis')
plt.colorbar()
plt.xticks(np.arange(6), df.corr().columns.values)
plt.yticks(np.arange(6), df.corr().columns.values)
plt.show()

png

# seaborn을 활용하여 상관계수를 시각화해보기
import seaborn as sns
sns.heatmap(df.corr(), annot=True)
plt.show()

png

피드백 (프로젝트1)

  • 전체적으로 완벽하게 EDA를 수행하셨습니다.
  • 특별히 수정이나 코멘트가 필요없을거 같습니다.

데이터 분석 Project (2)

문제 : 주어진 데이터를 활용하여 2010년 인구 밀도 기준으로 미국 주와 지역 순위를 계산하세요. (인구 밀도의 내림차순)

필요한 데이터를 로딩합니다.

population = pd.read_csv('./datasets/state-population.csv')
areas = pd.read_csv('./datasets/state-areas.csv')
abbrevs = pd.read_csv('./datasets/state-abbrevs.csv')

간단하게 데이터를 확인하고 데이터의 shape을 확인합니다.

# 인구에 대한 데이터
population.head()
state/region ages year population
0 AL under18 2012 1117489.0
1 AL total 2012 4817528.0
2 AL under18 2010 1130966.0
3 AL total 2010 4785570.0
4 AL under18 2011 1125763.0
# 넓이에 대한 데이터
areas.head()
state area (sq. mi)
0 Alabama 52423
1 Alaska 656425
2 Arizona 114006
3 Arkansas 53182
4 California 163707
# 지역 약자에 대한 데이터
abbrevs.head()
state abbreviation
0 Alabama AL
1 Alaska AK
2 Arizona AZ
3 Arkansas AR
4 California CA
# 데이터의 shape
print(population.shape)
print(areas.shape)
print(abbrevs.shape)
(2544, 4)
(52, 2)
(51, 2)

Population 데이터에 State full name 데이터를 outer 조인 시킵니다. 왼쪽 dataframe의 키는 state/region이고 오른쪽 dataframe의 키는 abbreviation입니다.

merged = pd.merge(population, abbrevs, how='outer', \
                  left_on ='state/region', right_on ='abbreviation')
merged.head()
state/region ages year population state abbreviation
0 AL under18 2012 1117489.0 Alabama AL
1 AL total 2012 4817528.0 Alabama AL
2 AL under18 2010 1130966.0 Alabama AL
3 AL total 2010 4785570.0 Alabama AL
4 AL under18 2011 1125763.0 Alabama AL

Null 값을 체크합니다.

merged.isnull().any()
state/region    False
ages            False
year            False
population       True
state            True
abbreviation     True
dtype: bool

State full name(컬럼명 : state)의 Null값을 확인합니다.

merged[merged['state'].isnull()].head()
state/region ages year population state abbreviation
2448 PR under18 1990 NaN NaN NaN
2449 PR total 1990 NaN NaN NaN
2450 PR total 1991 NaN NaN NaN
2451 PR under18 1991 NaN NaN NaN
2452 PR total 1993 NaN NaN NaN

PR 지역의 state 항목(full name)이 누락되어 있음을 볼 수 있습니다.

Area 정보는 state의 full name을 사용하기 때문에, 누락된 값을 처리해야합니다.

State full name 항목(컬럼명 : state)이 누락된 곳의 state/region을 살펴보겠습니다.

merged.loc[merged['state'].isnull(), 'state/region'].unique()
array(['PR', 'USA'], dtype=object)

우리의 지식(사전 정보를 이용하면) 누락된 데이터를 쉽게 채워넣을 수 있습니다.

merged.loc[merged['state/region'] == 'PR', 'state'] = 'Puerto Rico'
merged.loc[merged['state/region'] == 'USA', 'state'] = 'United States'

다시 한 번 null 값을 확인합니다.

merged.isnull().any()
state/region    False
ages            False
year            False
population       True
state           False
abbreviation     True
dtype: bool

State full name에 null값이 없음을 확인했습니다. 따라서 다른 값에 null이 있어도 우선 넘어갑니다.

다음으로, area 정보가 있는 dataframe을 merge 합니다.

final = pd.merge(merged, areas, on='state', how='left')
final.head()
state/region ages year population state abbreviation area (sq. mi)
0 AL under18 2012 1117489.0 Alabama AL 52423.0
1 AL total 2012 4817528.0 Alabama AL 52423.0
2 AL under18 2010 1130966.0 Alabama AL 52423.0
3 AL total 2010 4785570.0 Alabama AL 52423.0
4 AL under18 2011 1125763.0 Alabama AL 52423.0

Null 값을 다시 확인해봅니다.

final.isnull().any()
state/region     False
ages             False
year             False
population        True
state            False
abbreviation      True
area (sq. mi)     True
dtype: bool

Area에 null 값이 존재합니다. 면적을 계산할 때 area 컬럼이 필요하기 때문에 null값이 존재하면 안됩니다.

어떤 값이 null값을 가지는지 확인해봅니다.

final['state'][final['area (sq. mi)'].isnull()].unique()
array(['United States'], dtype=object)

United States의 null 값을 다루는 방법은 2가지중 1개로 사용할 수 있습니다.

  • Area의 전체 합(미국 전체의 면적이므로)로 값을 채운다.
  • 지역별 인구밀도를 구하는 문제이므로(전체 면적에 대한 정보가 필요없으므로) 그 데이터를 삭제한다.

사실, 주어진 문제를 해결하기 위해서는 2번째 방법을 선택하면 됩니다.

final.dropna(inplace = True)

Null 값을 다시 한 번 확인합니다.

final.isnull().any()
state/region     False
ages             False
year             False
population       False
state            False
abbreviation     False
area (sq. mi)    False
dtype: bool

더 이상 null 값이 존재하지 않습니다. 2010 인구밀도를 계산하기 위해, year와 ages를 살펴보겠습니다.

final['year'].unique()
array([2012, 2010, 2011, 2009, 2013, 2007, 2008, 2005, 2006, 2004, 2003,
       2001, 2002, 1999, 2000, 1998, 1997, 1996, 1995, 1994, 1993, 1992,
       1991, 1990], dtype=int64)
final['ages'].unique()
array(['under18', 'total'], dtype=object)

2010전체 나이대(total)에 대한 데이터를 가지고 옵니다.

data_2010 = final.query("year == '2010' & ages == 'total'")
data_2010.head()
state/region ages year population state abbreviation area (sq. mi)
3 AL total 2010 4785570.0 Alabama AL 52423.0
91 AK total 2010 713868.0 Alaska AK 656425.0
101 AZ total 2010 6408790.0 Arizona AZ 114006.0
189 AR total 2010 2922280.0 Arkansas AR 53182.0
197 CA total 2010 37333601.0 California CA 163707.0

Index를 지정하여 최종결과값이 Series가 되어, key가 도시이름이 되도록 합니다.

data_2010.set_index('state', inplace=True)
data_2010.head()
state/region ages year population abbreviation area (sq. mi)
state
Alabama AL total 2010 4785570.0 AL 52423.0
Alaska AK total 2010 713868.0 AK 656425.0
Arizona AZ total 2010 6408790.0 AZ 114006.0
Arkansas AR total 2010 2922280.0 AR 53182.0
California CA total 2010 37333601.0 CA 163707.0

인구 밀도를 계산합니다.

density = data_2010.population / data_2010['area (sq. mi)']
density.head()
state
Alabama        91.287603
Alaska          1.087509
Arizona        56.214497
Arkansas       54.948667
California    228.051342
dtype: float64

인구 밀도 순서대로 정렬하도록 합니다.

density.sort_values(ascending=False, inplace=True)
density.head()  # 인구밀도가 높은 지역
state
District of Columbia    8898.897059
New Jersey              1009.253268
Rhode Island             681.339159
Connecticut              645.600649
Massachusetts            621.815538
dtype: float64

피드백 (프로젝트2)

  • 프로젝트1과 마찬가지로 완벽한 수준으로 EDA를 수행하셨습니다.
  • Pandas의 수많은 기능과 시각화 기능을 구글링하시어,
  • 본인만의 관점과 방향으로 추가 EDA를 해보시는 연습도 좋을거 같습니다.
  • 수고 많으셨습니다.