즐겁게!! 자신있게!! 살아보세!!

재밌는 인생을 위하여! 영촤!

Language_Study/Python

[Python] 11-1.DataFrame응용과 시각화

Godwony 2021. 1. 19. 21:26
728x90
반응형

DataFrame응용과 시각화

함수매핑

  • pandas에서는 Series 나 DataFrame의 개별 요소에 동일한 함수를 적용하고자 하는 경우에 apply 메소드를 이용합니다.

1. Series가 apply 메소드를 호출하면 각 요소에게 함수를 적용한 후 그 결과를 가지고 다시 Series를 만들어서 리턴

2. DataFrame에 적요을 하게 되면 적용방법에 따라 Series가 되기도 하고 DataFrame이 리턴되기도 합니다.

  • axis 옵션을 이용해서 열과 행단위로 데이터를 대입할 수 있습니다.

  • axis=0이 기본값으로 열단위로 댕비하는데 1을 설정하면 행단위로 함수에 대입

  • Series는 하나의 열의 개념이고 DataFrame은 행열(Matrix - Table)

  • 머신러닝에서는 데이터의 단위가 대부분 Matrix 입니다.

DataFrame['컬럼이름'] : Series
DataFrame[['컬럼이름']] : DataFrame (머신러닝)
pandas 자료구조에서 .values 하게되면 컬럼이름을 제거하고 데이터만으로 구성된 numpy.ndarray를 리턴합니다.

  • 머신러닝에서는 컬럼이름이 필요없습니다.
import seaborn as sns 
import numpy as np 
import pandas as pd 

# seaborn 패키지에 존재하는 titanic 데이터 가져오기
# 인터넷에서 가져오기 때문에 인터넷 안되면 안됨.
# seaborn 패키지 : 샘플 데이터 셋과 matplotlib보다 시각적인 효과가 뛰어난 그래프를 만들어주는 패키지 
# numpy, pandas, matplotlib, seaborn, sklearn(전처리 preprocessing, 머신러닝)

titanic = sns.load_dataset('titanic')
titanic.info()


# %%
# 2개의 컬럼을 추출해서 새로운 DataFrame 만들기
# age, fare 
df = titanic[['age', 'fare']]
print(df.info())



# 행 전체의 age와 fare 열 가져오기 
df = titanic.loc[:, ['age', 'fare']]
df.info()


# %%
def add_10(x) : 
    return x+10

# 데이터 프레임에 함수 적용 - 열 단위로 적용 
result = df.apply(add_10)

print(result.head())
print(result.info())

DataFrame 결합

1. merge()

  • 2개의 DataFrame이나 Series를 합치기 위한 함수

  • 첫번째와 두번째 매개변수는 합치기 위한 Series나 DataFrame을 설정해야 하는데 left와 right 매개변수로도 대입해도 됩니다.

  • 하나 이상의 key를 가지고 결합

  • 관계형 데이터베이스의 join과 유사

  • 별다른 설정이 없으면 동일한 컬럼이름을 찾아서 join

  • 직접 join key를 지정하고자 하면 on 매개변수에 컬럼이름을 설정

  • 양쪽의 key 이름이 다른 경우에는 left_on 그리고 right_on 매개변수를 이용해서 직접 key를 설정

  • 기본 merge는 inner join의 개념 ( 양쪽 모두에 존재하는 데이터만 merge)

  • 어느 한쪽에만 존재하는 데이터를 merge에 참여시키고자 할 때는 how라는 매개변수에 left, right, outer 중에 하나를 설정합니다. 이러한 join 방식을 Outer join 이라고 한다.

  • key 가 아닌 컬럼 중에 양쪽에 동일한 이름으로 존재하는 컬럼이 있으면 이때는 _x_y라는 이름이 자동삽입되는데 이것을 변경하고자 하면 suffixes 옵션에 튜플로 추가될 이름 2개를 설정하면 된다.

  • 양쪽에 동일한 의미를 갖는 컬럼이 없는 경우 index를 이용해서 join을 할 수 있는데 이 경우에는 left_index=True 그리고 right_index=True 를 추가해주면 된다.

  • merge를 수행하면 key 값으로 정렬을 수행하는데 sort 옵션에 false를 설정하면 정렬하지 않습니다.

# To add a new cell, type '# %%'
# To add a new markdown cell, type '# %% [markdown]'
# %%
import pandas as pd 

# stock price.xlsx 파일과 stock valuation.xlsx 파일 읽기
price = pd.read_excel('../data/stock price.xlsx')
valuation = pd.read_excel('../data/stock valuation.xlsx')

print(price.info())
print(valuation.info())


# %%
# 2개의 Dataframe을 id를 기준으로 합치기 
merge_inner = pd.merge(price, valuation)
print(merge_inner.info()) 
# 결과물을 보니 데이터가 5개로 줄어들었다.
# 양쪽 모두에 존재하는 데이터만 합치기 - Inner join 이라고한다.


# %%
# Outer join - 어느 한쪽에만 존재하는 데이터도 join에 참여 
merge_outer = pd.merge(price, valuation, how='outer', on='id')
print(merge_outer.info())
print(merge_outer)

2. join

  • 인덱스를 기준으로 합쳐주는 함수

DataFrame.join(다른 데이터프레임)

import pandas as pd 

# stock price.xlsx 파일과 stock valuation.xlsx 파일 읽기
# 파일의 내용을 읽을 때 하나의 컬럼을 인덱스로 설정
price = pd.read_excel('../data/stock price.xlsx', index_col='id')
valuation = pd.read_excel('../data/stock valuation.xlsx', index_col='id')

print(price.info())
print(valuation.info())


# 인덱스를 이용해서 join 
stock_join = price.join(valuation)
print(stock_join)
stock_join.info()
# price 의 모든 데이터는 join에 참여 하는데 
# valuation 에서는 참여하지 않는 데이터도 있을 수 있다. 

3. concat

  • 구성형태와 속성이 균일한 경우 행 또는 열방향으로 이어 붙이는 함수
  • merge 나 join은 행방향으로 결합
  • join 옵션에 inner를 적용하면 양쪽에 모두 존재하는 데이터끼리 결합하고 outer를 설정하면 한쪽에만 존재하는 데이터도 결합을 하는데 기본값이 outer
  • axis 옵션에 0을 설정하면 열방향(위아래)으로 합치고 1을 설정하면 행방향(좌우)로 합침
# To add a new cell, type '# %%'
# To add a new markdown cell, type '# %% [markdown]'
# %%
import pandas as pd 

# concat - 열 행 방향으로 DataFrame을 합쳐주는 함수 

df1 = pd.DataFrame({'a': ['a0','a1', 'a2']}, index=[1,2,3])
df2 = pd.DataFrame({'a': ['a2','a3', 'a4'], 'b':['b2', 'b3','b4']}, index=[2,3,4])

print(df1)
print(df2)


# %%
# 열방향(위 아래)로 합침
print(pd.concat([df1,df2]))


# %%
# 행방향(좌우)로 합치기
print(pd.concat([df1,df2], axis=1))


# %%
# 한쪽에만 존재하는 데이터 제거 
print(pd.concat([df1, df2], axis=1, join='inner'))

4. append

  • 인덱스가 별의미 없는 경우 무조건 행방향으로합치는 함수

5. combine_first

  • 양쪽에 겹쳐지는 인덱스가 존재하는 경우에 무조건 호출하는 쪽의 값으로 설정
import pandas as pd 

df1 = pd.DataFrame({'a': ['a0','a1', 'a2']}, index=[1,2,3])
df2 = pd.DataFrame({'a': ['a2','a3', 'a4'], 'b':['b2', 'b3','b4']}, index=[2,3,4])

# %%
# 행방향으로 무조건 합치기 
print(df1.append(df2))


# %%
# 인덱스를 가지고 합치는데 호출하는 쪽의 데이터를 우선 적용 
print(df1.combine_first(df2))

그룹화

  • DataFrame의 내용을 그룹 단위로 분할해서 탐색하는 것
  • 그룹화를 한 후 집계 변환, 필터링을 적용
  • 분할을 위해서는 groupby함수를 이용하고 변환에는 apply 함수를 적용

1. groupby 함수

  • 그룹화할 컬럼이름을 대입하면 그룹화를 수행, 컬럼이름은 1개도 되고 컬럼이름의 list도 가능

  • 그룹별 데이터를 별도가 가져오고자 하는 경우에는 get_group(그룹이름)

  • 빠른 열거를 이용하면 그룹화할 때 사용한 키와 데이터를 튜플로 접근을 할 수 있다.

    • 파이썬은 튜플을 나누어서 저장 가능하다.
  • 집계함수(개수, 합계, 평균, 최대값, 최소값, 표준편차, 분산, 중간값, 사분위수) 사용 가능

  • maen(), max(), min(), sum(), count(), size(), var(), std(), first(), last(), descrive() - 기술통계에 대한 자료정보, info() - 자료요약 등

  • apply() 함수를 호출해서 변환 가능 - 매개변수 1개를 받아서 return 하는 함수를 대입

  • filter()를 호출해서 원하는 데이터만 추출 가능 - 매개변수 1개를 받아서 bool을 리턴하는 함수를 대입

  • 직접 만든 함수를 대입해서 결과를 얻고자 하는 경우에는 agg 함수를 이용

    • agg 함수에는 함수의 list를 대입해도 된다.
# To add a new cell, type '# %%'
# To add a new markdown cell, type '# %% [markdown]'
# %%
import pandas as pd 
import seaborn as sns 

# titanic 데이터 가져오기 
titanic = sns.load_dataset('titanic')
print(titanic.info())


# %%
# class 별로 그룹화 
grouped=titanic.groupby('class')
print(grouped) 
# 출력값 : 어떤 클래스 이름의 객체로 나온다. 


# %%
# 그룹화된 데이터에 접근
for key, data in grouped : 
    print(key,'\n')
    print(data.head(2),'\n')
# 출력값 : first, second, third 그룹이 생성됨.


# %%
# Third 그룹에 해당하는 그룹의 데이터만 가져오기 
third = grouped.get_group('Third')
print(third['class'])


# %%
# 집계 함수 적용
print(grouped.mean())


# %%
# 2개의 특성으로 그룹화해서 집계
grouped = titanic.groupby(['class', 'sex'])  # 범주형 데이터를 넣어야됨, 숫자 안됨. 
print(grouped.mean())


# %%
# 그룹화 해서 원하는 함수를 적용 (최대값-최소값)
def func(x) : 
    return x.max() - x.min()

grouped = titanic.groupby(['class'])
print(grouped.agg(func))


# %%
# 여러개의 함수를 적용
# 분석 보고서를 그룹별로 만들면 유용하다.
print(grouped.agg(['max', 'min']))


# %%
# 각 그룹별 데이터 개수 확인
for key, data in grouped : 
    print(key, len(data))

# 결과값 First 216, Second 184, Third 491


# %%
# 데이터가 200개 안되는 그룹은 제거 
# 데이터의 개수가 200이상인 여부를 알려주는 함수
def over200(x):
    return len(x) >= 200

# grouped_filter = grouped.filter(over200)
print(grouped_filter['class'])
#결과값을 보면 second 클래스가 제외됐다.


# %%
# 위의 내용을 람다함수로 변환
# 파이썬에서의 람다는 이름없는 한 줄 짜리 함수
# 필터링이나 mapping(apply) 메소드에서 람다를 많이 사용 
grouped_filter = grouped.filter(lambda x:len(x)>200)
print(grouped_filter['class'])


# %%
# age 열의 평균이 30이 안되는 그룹을 제거 
for key, data in grouped : 
    print(key, data['age'].mean())


# %%
grouped_filter = grouped.filter(lambda x: x['age'].mean()>=30)
print(grouped_filter['class'])

2. 멀티 인덱스

  • 인덱스를 여러 그룹으로 설정한것
  • 멀티 인덱스로 설정된 데이터모임에서 특정한 인덱스의 데이터를 가져오고자 하면 loc[(첫번째 인덱스값, 두번째 인덱스값,...)]
  • xs 함수를 이용할 수 있는데 이 경우는 첫번째 매개변수로 인덱스 이름을 대입하고 level에 인덱스의 값을 대입한다.
# 위 코드 이어서 
# 그룹화 할 때 2개의 이상의 컬럼 이름을 대입하면 멀티인덱스
grouped = titanic.groupby(['class', 'sex'])
gdf = grouped.mean()
print(gdf)


# %%
# 원하는 값을 출력하고 싶을 때 
# 행단위로 데이터 접근
print(gdf.loc['Third'])
# 결과값 : Third 인덱스로 속해 있는 값들이 출력된다. 


# %%
# Third 의 male만 알고싶다.
print(gdf.loc[('Third', 'male')])


# %%
# xs 인덱스 사용
print(gdf.xs('male', level='sex'))
# 첫번째 매개변수와, level 은 인덱스이름

3. pivot_table()

  • 데이터를 그룹화하기 위한 함수
  • 데이터를 행과 열 단위로 그룹화해서 기술 통계 값을 출력해주는 함수
pandas.pivot_table(데이터프레임, 
    values=[연산을 수행할 컬럼이름 나열], 
    index=[index로 사용할 컬럼이름 나열], 
    columns=[컬럼으로 사용할 이름나열],  # 여기까지가 기본 
    margins=전체데이터 출력여부,    # bool 형식
    aggfunc=수행할함수,
    fill_value=NA인 경우의 대체값)
  • pivot_table 함수를 호출하면 인덱스는 대부분의 경우 멀티 인덱스로 생성되는데 해제하고자 하는 경우에는 reset_index를 호출하면 된다.
import numpy as np 
import pandas as pd 
import seaborn as sns 

# seaborn에 존재하는 titanic 데이터 가져오기 
titanic = sns.load_dataset('titanic')
print(titanic)
titanic.info()


# %%
# age, sex, class, fare, survived 컬럼만 추출해서 새로운 데이터프레임 생성
# 방법1
df = titanic[['age', 'sex', 'class', 'fare', 'survived']]
print(df)


# %%
# 방법2
df = titanic.loc[:,['age', 'sex', 'class', 'fare', 'survived']]
print(df)


# %%
pivot1 = pd.pivot_table(df, 
                        values=['age'], 
                        index=['class'], 
                        columns=['sex'], 
                        aggfunc='mean')
print(pivot1,'\n')

pivot2 = pd.pivot_table(df, 
                        values=['age'], 
                        index=['class'], 
                        columns=['sex'], 
                        aggfunc=['mean','sum'])
print(pivot2)


# %%
# 멀티 인덱스를 마들기 위한 pivot_table 옵션 설정 
pivot3 = pd.pivot_table(df, 
                        values=['age', 'fare'], 
                        index=['class', 'sex'], 
                        columns=['survived'], 
                        aggfunc=['mean','sum'])
print(pivot3)


# %%
# 첫번째 인덱스(class) 가 First인 데이터 가져오기
print(pivot3.xs('First'))


# %%
# First 이고 male 인 데이터 가져오기 
print(pivot3.xs(('First', 'male'), level=['class','sex']))


# %%
# mean 열의 데이터만 가져오기  (반대방향이라 axis=1옵션)
print(pivot3.xs('mean', axis=1))

응용예제

서울시 구별 CCTV(자치구별 인구수) 개수와 인구수를 시각화

1. 데이터 가져오기

  • <data.seoul.go.kr> 사이트에서 서울시와 관련된 데이터를 가져오기
  • cctv 개수 -> cctv.xlsx
  • 인구 데이터 -> pop.txt

2. python에서 불러오기

3. 컬럼이름 수정하고 구이름을 동일한 형태로 만들기.

4. 불필요한 컬럼이나 열을 제거

  • 컬럼이나 열을 제거하는 방법은 현재 데이터프레임에서 직접 제거(drop 함수 이용)
  • 필요한 컬럼이나 열만 추출

5. 두개의 데이터프레임을 합치기

6. 합쳐진 데이터프레임에서 불필요한 컬럼 제거

  • 2011년 이전, 2012년, 2013년, 2014년, 2015년, 2016년, 2017년, 기간

7. 구별 컬럼을 인덱스로 설정

8. 그래프 그리기

9. 인구수(계)와 cctv개수(소계)를 가지고 산포도 그리기

10. numpy의 polyfit 이라는 함수를 이용해서 다항식을 구해서 선 그래프 그리기

  • 다항식 -> 회귀식

11. 산포도의 각 점에 자신의 이름을 표시하고 잔차를 점의 색상으로 설정

  • 잔차 - 실제데이터와 예측된 데이터와의 차이
# To add a new cell, type '# %%'
# To add a new markdown cell, type '# %% [markdown]'
# %%
import pandas as pd 
import numpy as np 

# 1. 서울시 구별 CCTV와 인구수 시각화 
# 2. cctv.xlsx 파일과 pop.txt 파일의 내용 읽기

cctv = pd.read_excel('../data/cctv.xlsx')
print(cctv)


# %%
# read_csv 로 불러올때는 확장자명이 의미가 없다. txt 도 불러올수있다. 
# pop = pd.read_csv('../data/pop.txt')
# print(pop) 

# 데이터를 까보자 txt파일
# 데이터가 , 가 아니라 tab 으로 구분되어있다. 
# 값들이 천단위기호가 붙어있다.
# 컬럼이 세줄이다. (두줄을 건너뛰어야된다) 


# %%
# delimiter='\t' 구분자는 탭 
# thouansds=',' 천단위 구분기호 ,
# skiprows=2 컬럼 두줄 건너뛰고 읽기

pop = pd.read_csv('../data/pop.txt',
                    delimiter='\t',
                    skiprows=2,
                    thousands=',')
print(pop) 


# %%
# cctv.xlsx 의 구이름이 한칸씩 띄어져 있다. 
# pop.tvt의 구이름이 두번째 부터있다.
# 3. 컬럼이름 수정하고 구이름을 동일한 형태로 만들기.

# inplace=True 가 설정되면 현재 데이터프레임에 적용하고 
# 이 옵션이 없으면 현재 데이터프레임을 복사해서 작업하고 
# return을 해준다. 
cctv.rename(columns={cctv.columns[0]:'구별'}, inplace=True)
pop.rename(columns={pop.columns[1]:'구별'}, inplace=True)

#cctv의 구별 데이터에서 공백을 전부 제거하기 
gu = [] 
for x in cctv['구별'] : 
    gu.append(x.replace(' ',''))
cctv['구별'] = gu
print(cctv['구별'])


# %%
# 4. 불필요한 컬럼이나 열을 제거 

# 첫번째 방법 : pop에서 필요한 행과 열만 가져오기(기간, 구별, 계, 남자, 여자)
pop = pop[['기간', '구별', '계', '남자', '여자']]
print(pop)


# %%
# 두번째 방법 : 0번 행을 제거 
pop.drop([0], inplace=True)

# 여성비율이라는 새로운 컬럼을 추가 - 여자/계 * 100
pop['여성비율'] = pop['여자']/pop['계'] * 100
print(pop)


# %%
# 5. 두개의 데이터프레임 합치기 
# cctv와 pop를 합치기 - 기준은 구별로 

df=pd.merge(cctv, pop, on='구별')
print(df)


# %%
# 6. 합쳐진 데이터프레임에서 불필요한 컬럼 제거 
# 2011년 이전, 2012년, 2013년, 2014년, 2015년, 2016년, 2017년, 기간
# drop으로 지울때 주의 요망(그냥지우면 행이지워짐. 뒤에 옵션들을 잘 기억하자.)
df.drop(['2011년 이전', '2012년', '2013년', '2014년', '2015년', '2016년', '2017년', '기간'], axis=1, inplace=True)
print(df)


# %%
# 7. 구별 컬럼을 인덱스로 설정
df.set_index('구별', inplace=True)
print(df)


# %%
# 8. 그래프 그리기 위한 패키지 
import matplotlib.pyplot as plt 

# 그래프에서 한글 처리를 위한 패키지 
from matplotlib import font_manager, rc
import platform

if platform.system() == 'Darwin':
    rc('font', family='AppleGothic')
elif platform.system() == 'Windows':
    font_name = font_manager.FontProperties(fname="c:/Windows/Fonts/malgun.ttf").get_name()
    rc('font', family=font_name)


# %%
# cctv 개수를 가지고 막대 그래프 그리기 (kin=barh 로 하면 세로그래프)
df['소계'].plot(kind='bar', figsize=(10,10))
plt.show()


# %%
# 정렬을 해서 보이는 것도 나쁘진않다. 
df['소계'].sort_values().plot(kind='bar', figsize=(10,10))
plt.legend()
plt.show()


# %%
# 9.인구수(계)와 cctv개수(소계)를 가지고 산포도 그리기 
plt.figure(figsize=(8,8))
plt.scatter(df['계'], df['소계'], s=50)
plt.xlabel('인구수')
plt.ylabel('cctv개수')
plt.grid()
plt.show()

#회귀분석 할때는 이렇게 까지 쓸 필요없다


# %%
# 10. numpy의 polyfit 이라는 함수를 이용해서 다항식을 구해서 선 그래프 그리기

# 계와 소계의 데이터를 가지고 다항식 구하기 
# 뒤에 1은 1차다항식, 2는 2차다항식(숫자가 커지면 정확도가 올라가지만 계산이 오래걸림)
fp1 = np.polyfit(df['계'], df['소계'], 1)
f1 = np.poly1d(fp1)
print(f1)

# 출력값 : 0.002878 x + 1067 
# 출력값 : 기울기       절편 


# %%
# 선그래프 그리기 
# (X축 인구수를보고)100000 부터 700000 까지를 100개로 분할한 배열을 생성 
fx = np.linspace(100000, 700000, 100)
plt.figure(figsize=(8,8))
plt.scatter(df['계'], df['소계'], s=50)
plt.plot(fx, f1(fx), ls='dashed', lw=3, color='g')
plt.xlabel('인구수')
plt.ylabel('cctv개수')
plt.grid()
plt.show()


# %%
# 11. 산포도의 각 점에 자신의 이름을 표시하고 잔차를 점의 색상으로 설정

# 잔차를 색상으로 표시하고 지역 이름을 점위에 출력 
# 잔차 계산
df['잔차'] = np.abs(df['소계'] - f1(df['계']))
plt.figure(figsize=(14,12))
plt.scatter(df['계'], df['소계'], c=df['잔차'], s=50)
plt.plot(fx, f1(fx), ls='dashed', lw=3, color='g')

# 그래프에 텍스트 출력하기
for n in range(25) : 
    # 각 점들의 좌표 우측 
    plt.text(df['계'][n] * 1.02, 
             df['소계'][n] * 1, 
             df.index[n], 
             fontsize=12)

plt.xlabel('인구수')
plt.ylabel('cctv개수')
plt.colorbar()
plt.grid()
plt.show()
728x90
반응형

'Language_Study > Python' 카테고리의 다른 글

[Python] 12.통계학  (0) 2021.01.19
[Python] 11-2.Dataframe응용과 시각화  (0) 2021.01.19
[Python] 10-2.데이터전처리  (0) 2021.01.19
[Python] 10-1.데이터전처리  (4) 2021.01.19
[Python] 9.시각화  (0) 2021.01.19