이번 포스팅에서는 정답 레이블 Y는 없고 오직 설명변수 X 만을 사용해서 데이터 내 관계, 패턴, 구조를 탐색하는데 사용하는 비지도학습 중에서 차원 축소 (Dimensionality Reduction) 에 대한 이론적인 부분을 알아보겠습니다. (Python 코딩은 다음번 포스팅부터 해볼께요.)

 

1. 차원 축소란 무엇인가? (What is Dimensionality Reduction?)

2. 왜 차원 축소가 필요한가? (Why is Dimensionality Reduction?) 

3. 차원 축소의 단점은? (Pitfall of Dimensionality Reduction)

4. 차원 축소하는 방법은? (How to do Dimensionality Reduction?)

 

 

 

1. 차원 축소란 무엇인가? (What is Dimensionality Reduction?)

 

차원 축소 (Dimensionality Reduction, 또는 Dimension Reduction) 는 저차원 표현(low-dimensional representation)이 고차원 원본 데이터의 의미 있는 특성을 이상적으로 원래의 차원에 가깝게 유지할 수 있도록 고차원 공간에서 저차원 공간으로 데이터를 변환(the transformation of data from a high-dimensional space into a low-dimensional space)하는 것을 말합니다.[wikipedia, 1]

차원 축소를 하게 되면 원본 데이터로부터 일부 정보 손실 (information loss)이 발생하는데요, 원본 데이터로부터의 정보 손실을 최소화하면서 저차원으로 얼마나 잘 재표현(representation)할 수 있느냐가 관건이 되겠습니다. 

 

 

[ 차원 축소 (Dimensionality Reduction) vs. 군집 분석 (Clustering) ]

dimensionality reduction vs. clustering

 

차원 축소 (Dimensionality Reduction)와 군집분석 (Clustering) 모두 정답 Label Y 가 필요없이 특성 변수(features) 만을 가지고 데이터의 구조와 특성을 파악하는 비지도학습(Unsupervised Learning) 에 속합니다. 

 

군집분석 (Clustering) 은 관측치, 객체 간의 유사성(similarity)을 측정해서 유사한 관측치, 객체끼리 그룹을 만드는 분석 기법을 말합니다. 반면에 차원 축소 (Dimensionality Reduction) 는 특성 변수(features, variables)를 대상으로 변수 간 상관성에 기초해서 고차원에서 저차원 공간으로 재표현하는 변수 변환을 말합니다. 

 

 

 

2. 왜 차원 축소가 필요한가? (Why is Dimensionality Reduction?) 

 

차원 축소를 하는 이유, 활용 목적에는 여러가지가 있는데요, 먼저 기계학습 측면에서는 차원 축소가 차원의 저주 (Curse of Dimensionality)를 피하고, 과적합 (Overfitting) 을 방지하는데 효과적입니다. 

 

차원의 저주(Curse of Dimensionality) 는 일상적 경험의 3차원 물리적 공간 등 저차원적 환경에서 일어나지 않는 고차원적 공간에서 데이터를 분석하고 정리할 때 발생하는 다양한 현상을 말합니다. 이 표현은 Richard E. Bellman 이 동적 프로그래밍의 문제를 고려할 때 처음 사용하였습니다. 차원 저주 현상은 수치 분석, 샘플링, 조합론, 기계 학습, 데이터 마이닝 및 데이터베이스와 같은 영역에서 발생합니다. 이러한 문제의 공통 주제는 차원성이 증가하면 공간의 부피가 너무 빠르게 증가하여 사용 가능한 데이터가 희박해진다는 것입니다. 신뢰할 수 있는 결과를 얻기 위해 필요한 데이터의 양은 차원성에 따라 기하급수적으로 증가하는 경우가 많습니다. 또한 데이터를 구성하고 검색하는 것은 종종 객체가 유사한 속성을 가진 그룹을 형성하는 영역을 감지하는 데 의존합니다. 그러나 고차원 데이터에서는 모든 객체가 여러 면에서 희박하고 유사하지 않아 일반적인 데이터 구성 전략이 효율적이지 못하게 됩니다. [wikipedia, 2]

 

통계학의 선형회귀모형에는 독립변수(independent variable, 혹은 설명변수 explanatory variable, 혹은 예측변수 predictor variable) 들 간의 독립을 가정합니다. (독립변수라는 이름 자체에 변수 간 독립을 명시함. ㅎㅎ)  그런데 모델링에 인풋으로 사용하는 설명변수 간 다중공선성(multicolleniarity)이 존재할 경우 추정된 회귀계수의 분산이 커져서 모델이 불안정하고 과적합에 빠지는 위험이 있습니다. 독립변수간 다중공선성(multicolleniarity)을 해결하는 몇가지 방법 중의 하나가 독립변수간 상관성에 기반해서 차원을 축소한 후에 회귀모형을 적합하는 방법입니다. 

 

탐색적 데이터 분석을 하는 단계에서 변수 간 관계를 파악하기 위해서 산점도(scatter plot)으로 시각화(visualization) 해서 보면 효과적인데요, 만약 특성변수(features)의 개수가 여러개일 경우에는 2개 특성변수 간 조합의 개수가 기하급수적으로 늘어나기 때문에 모든 조합을 시각화해서 살펴보는 것에 어려움이 있습니다.  이럴 경우, 차원축소를 해서 소수의 차원에 대해서 시각화를 하면 많은 양의 정보를 효과적으로 시각화해서 데이터 특성을 탐색해볼 수 있습니다. 

 

이밖에도 차원 축소를 하면 이후의 데이터 처리, 분석 시에 연산 속도를 향상(performance incease)시킬 수 있습니다. 그리고 데이터를 압축(data compression)하여 데이터 저장이나 전송 효율을 높이는데도 차원 축소를 사용할 수 있습니다. 

 

 

 

3. 차원 축소의 단점은? (Pitfall of Dimensionality Reduction)

 

차원 축소를 하게 되면 원본 데이터 대비 정도의 차이일 뿐 필연적으로 정보 손실 (Information Loss) 이 발생합니다. 그리고 원본 데이터 대비 차원 축소한 데이터를 해석하는데도 어려움이 (hard to interprete) 생깁니다. 또한 차원 축소를 위한 데이터 변환 절차가 추가되므로 데이터 파이프 라인 (data pipeline) 이 복잡해지는 단점도 있습니다. 

 

 

 

4. 차원 축소하는 방법은? (How to do Dimensionality Reduction?)

 

차원 축소하는 방법은 크게 (Linear) Projection(Non-linear) Manifold Learning 의 2가지로 나눌 수 있습니다. 

 

(4-1) Projection-based Dimensionality Reduction: 주성분분석 (PCA, Principal Component Analysis), 특이값 분해 (Singular Value Decomposition), 요인분석 (Factor Analysis)

 

(4-2) Manifold Learning: LLE (Locally-Linear Embedding), Isomap, Kernel Principal Component Analysis, Autoencoders, SOM(Self-Organizing Map) 

 

dimensionality reduction methods and algorithms

 

 

다음번 포스팅에서는 linear projection 방법 중에서 Python 의 Sklearn 모듈을 활용하여 주성분분석 (PCA, Principal Component Analysis)을 하는 방법을 소개하겠습니다. 

 

 

[ Reference ] 

[1] Wikipedia - Dimensionality Reduction: https://en.wikipedia.org/wiki/Dimensionality_reduction

[2] Wikipedia - Curse of Dimensionality: https://en.wikipedia.org/wiki/Curse_of_dimensionality

[3] Wikipedia - Nonlinear Dimensionality Reduction: https://en.wikipedia.org/wiki/Nonlinear_dimensionality_reduction

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요!  :-)

 

728x90
반응형
Posted by Rfriend
,

시각화를 하다보면 기존의 그래프에 가독성을 높이기 위해 여러가지 종류의 도형을 추가해야 할 때가 있습니다.  Python  matplotlib.artist 의 Patch 클래스는 아래의 화면 캡쳐에서 보는 것처럼 Shadow, Spine, Wedge, Circle, Ellipse, Arrow, Rectangle, RegularPolygon, FancyArrowPatch, PathPatch 등의 다양한 도형을 쉽게 그릴 수 있는 클래스를 지원합니다. 

 

 

[ matplotlib artist inheritance diagrams, reference for matplotlib artists ]

 

matplotlib.artist Inheritance Diagrams and Reference for Matplotlib artists (* source: matplotlib.org)

 

이들 Reference for Matplotlib artists 중에서 이번 포스팅에서는 matplotlib의 add_patch() 함수matplotlib.patches 클래스의 다양한 artists 를 사용해서 

 

(1) matplotlib 그래프에 직사각형 추가하기 (adding a Rectangle in matplotlib's plot)

(2) matplotlib 그래프에 원 추가하기 (adding a Circle in matplotlib's plot)

(3) matplotlib 그래프에 타원 추가하기 (adding a Ellipse in matplotlib's plot)

(4) matplotlib 그래프에 다각형 추가하기 (adding a Polygon in matplotlib's plot)

(5) matplotlib 그래프에 화살표 추가하기 (adding a Arrow in matplotlib's plot)

 

하는 방법을 소개하겠습니다. 

 

 

먼저, 시각화를 위해 필요한 Python 모듈을 불러오고, 예제로 사용할 간단한 샘플 데이터셋을 만들어보겠습니다. 

 

## importing modules for visualization and data generation
import matplotlib.pyplot as plt
import matplotlib.patches as patches
import numpy as np


## generating sample datasets
np.random.seed(1004)
x1 = np.random.uniform(low=1.5, high=4.5, size=100)
y1 = np.random.uniform(low=1.5, high=2.5, size=100)
x2 = np.random.normal(loc=7, scale=0.6, size=100)
y2 = np.random.normal(loc=7, scale=0.6, size=100)
x = np.concatenate((x1, x2), axis=0)
y = np.concatenate((y1, y2), axis=0)

 

 

(1) matplotlib 그래프에 직사각형 추가하기 (adding a Rectangle in matplotlib's plot)

 

코드가 추가적인 설명이 필요 없을 정도로 간단하므로 예제 코드와 그 결과로 그려진 그래프 & 추가된 도형 위에 matplotlib.matches 클래스의 각 artist 별 매개변수를 표기해놓는 것으로 설명을 갈음하겠습니다. 

 

import matplotlib.pyplot as plt
import matplotlib.patches as patches

## plotting
fig, ax = plt.subplots(figsize=(7, 7))
ax.set_title('Adding a Rectangle in plot', fontsize=16)
ax.set_xlim(0.0, 10.0)
ax.set_ylim(0.0, 10.0)
ax.set_xlabel('X')
ax.set_ylabel('Y')

ax.plot(x, y, 'ro', alpha=0.5)

## (1) a Rectangle patch
## https://matplotlib.org/stable/api/_as_gen/matplotlib.patches.Rectangle.html
ax.add_patch(
   patches.Rectangle(
       (1.0, 1.0),      # (x, y) coordinates of left-bottom corner point
       4, 2,            # width, height
       edgecolor = 'blue',
       linestyle = 'dashed', 
       fill = True,
       facecolor = 'yellow',
   ))

plt.show()

adding a patch of a rectangle in matplotlib

 

 

 

(2) matplotlib 그래프에 원 추가하기 (adding a Circle in matplotlib's plot)

 

import matplotlib.pyplot as plt
import matplotlib.patches as patches

## plotting
fig, ax = plt.subplots(figsize=(7, 7))
ax.set_title('Adding a Circle in plot', fontsize=16)
ax.set_xlim(0.0, 10.0)
ax.set_ylim(0.0, 10.0)
ax.set_xlabel('X')
ax.set_ylabel('Y')

ax.plot(x, y, 'ro', alpha=0.5)

## (2) a Circle patch
## https://matplotlib.org/stable/api/_as_gen/matplotlib.patches.Circle.html
ax.add_patch(
   patches.Circle(
       (7.0, 7.0),      # (x, y) coordinates of center point
       radius = 2.,     # radius
       edgecolor = 'black',
       linestyle = 'dotted', 
       fill = True,
       facecolor = 'lightgray',
   ))

plt.show()

adding a patch of a circle in matplotlib

 

 

 

(3) matplotlib 그래프에 타원 추가하기 (adding a Ellipse in matplotlib's plot)

 

import matplotlib.pyplot as plt
import matplotlib.patches as patches

## plotting
fig, ax = plt.subplots(figsize=(7, 7))
ax.set_title('Adding an Ellipse in plot', fontsize=16)
ax.set_xlim(0.0, 10.0)
ax.set_ylim(0.0, 10.0)
ax.set_xlabel('X')
ax.set_ylabel('Y')

ax.plot(x, y, 'ro', alpha=0.5)

## (3) A scale-free ellipse.
## https://matplotlib.org/stable/api/_as_gen/matplotlib.patches.Ellipse.html
ax.add_patch(
   patches.Ellipse(
       xy = (5, 5), # xy xy coordinates of ellipse centre.
       width = 5,   # width Total length (diameter) of horizontal axis.
       height = 10, # height Total length (diameter) of vertical axis.
       angle = -40, # angle Rotation in degrees anti-clockwise. 0 by default
       edgecolor = 'black',
       linestyle = 'solid', 
       fill = True,
       facecolor = 'yellow',
   ))

plt.show()

adding a patch of an ellipse in matplotlib

 

 

 

(4) matplotlib 그래프에 다각형 추가하기 (adding a Polygon in matplotlib's plot)

 

다각형(Polygon)을 추가하려면 다각형의 각 꼭지점의 xy 좌표의 모음을 numpy array 형태로 입력해주면 됩니다. 그리고 closed=True 를 설정해주면 xy 좌표들의 모음의 처음 좌표와 마지막 좌표를 연결해서 다각형의 닫아주어서 완성해줍니다. 

 

import matplotlib.pyplot as plt
import matplotlib.patches as patches
import numpy as np

## plotting
fig, ax = plt.subplots(figsize=(7, 7))
ax.set_title('Adding a Polygon in plot', fontsize=16)
ax.set_xlim(0.0, 10.0)
ax.set_ylim(0.0, 10.0)
ax.set_xlabel('X')
ax.set_ylabel('Y')

ax.plot(x, y, 'ro', alpha=0.5)

## (4) a Polygon patch
## https://matplotlib.org/stable/api/_as_gen/matplotlib.patches.Polygon.html
ax.add_patch(
   patches.Polygon(
       # xy is a numpy array with shape Nx2.
       np.array([[1, 1], [5, 1], [9, 5], [9, 9], [6, 9], [1, 4]]), # xy
       closed=True,
       edgecolor = 'black',
       linestyle = 'dashdot', 
       fill = True,
       facecolor = 'lightgray',
   ))

plt.show()

adding a patch of a polygon in matplotlib

 

 

 

(5) matplotlib 그래프에 화살표 추가하기 (adding a Arrow in matplotlib's plot)

 

import matplotlib.pyplot as plt
import matplotlib.patches as patches

## plotting
fig, ax = plt.subplots(figsize=(7, 7))
ax.set_title('Adding an Arrow in plot', fontsize=16)
ax.set_xlim(0.0, 10.0)
ax.set_ylim(0.0, 10.0)
ax.set_xlabel('X')
ax.set_ylabel('Y')

ax.plot(x, y, 'ro', alpha=0.5)

## (5) a Arrow patch
## https://matplotlib.org/stable/api/_as_gen/matplotlib.patches.Arrow.html
ax.add_patch(
   patches.Arrow(
       x = 3, # x coordinate of the arrow tail.
       y = 5, # y coordinate of the arrow tail.
       dx = 0, # dx Arrow length in the x direction.
       dy = -2, # dy Arrow length in the y direction.
       # width: Scale factor for the width of the arrow. 
       width = 1, # With a default value of 1, the tail width is 0.2 and head width is 0.6.
       edgecolor = 'black',
       linestyle = 'solid', 
       fill = True,
       facecolor = 'yellow',
   ))

plt.show()

adding a patch of an arrow in matplotlib

 

 

 

[ Reference ]

* Python matplotlib's Inheritance Diagrams 

   : https://matplotlib.org/stable/api/artist_api.html#artist-api

* Reference for matplotlib artists

   : https://matplotlib.org/stable/gallery/shapes_and_collections/artist_reference.html#sphx-glr-gallery-shapes-and-collections-artist-reference-py 

* Python matplotlib's Rectangle patch
   : https://matplotlib.org/stable/api/_as_gen/matplotlib.patches.Rectangle.html

* Python matplotlib's Circle patch
   : https://matplotlib.org/stable/api/_as_gen/matplotlib.patches.Circle.html

* Python matplotlib's scale-free Ellipse
   : https://matplotlib.org/stable/api/_as_gen/matplotlib.patches.Ellipse.html

* Python matploblib's Polygon patch
   : https://matplotlib.org/stable/api/_as_gen/matplotlib.patches.Polygon.html

* Python matplotlib's Arrow patch
   : https://matplotlib.org/stable/api/_as_gen/matplotlib.patches.Arrow.html

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요!  :-)

 

728x90
반응형
Posted by Rfriend
,

요즘 회사일이 숨 쉴 틈도 없이 프로젝트의 연속인지라 책을 읽을 여유도 없고, 책을 읽어도 회사 일 관련된 데이터 분석 알고리즘, 프로그래밍 등과 관련된 분야의 책들만 읽었어요. 흑... ㅜ.ㅜ  그러다가 설 연휴 맞이해서 모처럼 에세이 책 읽어보았네요. 책상에 어지럽게 쌓여있는 책들 중에서 제목이랑 표지가 뭔가 젊은 감성이 물씬 풍기고 세련되어 보여서 집어 들었어요. 

 

"시키는 대로 제멋대로" (이소호 에세이, 창비)

 

"시키는 대로" 부분에 밑줄 쫘악~!  ㅋㅋ  요즘 젊은 작가는 제목에서 부터 이런 센스를 부리네요!  

"제멋대로"도 빼뚤빼뚤 제멋대로예요. 

 

시키는 대로 제멋대로, 이소호 에세이, 창비

 

저자는 자신을 "시인 이소호"로 소개를 하네요.  "이경진"에서 "이소호"로 개명을 했다고도 하구요. 

 

책을 펼치면 첫 장이 시인 이소호씨가 (이경진의 이름이었던) 초등학생 때 썼던 일기로 시작을 해요. 그것도 "우리 가족"에 대한 일기예요.  느낌 오지 않나요? 이 수필집을 관통하는 키워드를 하나 꼽으라면 이런것도 독자를 대상으로 해서 글을 써도 되나 싶을 정도로 강박적이고 수위가 높은 "솔직함"이예요. 일기, 그리고 가족 이야기 만큼 개인적이고 그래서 타인에게 솔직하게 드러내기가 쉽지 않은 부분이 있을까요?  이소호 시인은 "우리 가족"에 대한 "일기"를 공개하는 것으로 수필집의 첫 장을 열었어요. 말 다했죠! 

 

"솔직한 글쓰기"는 누군가에게는 '나만 힘든게 아니었구나'하는 위안이 되기도 하고, 누군가에게는 '나만 이렇게 생각한게 아니었구나'하고 공감을 불러일으키기도 하는 힘이 있는 것 같아요. 반면에 홍상수 영화를 볼 때 느껴지는 있는 그대로의 맨 날 것의 생생한 모습을 직접 보게 되었을 때의 불편함도 동반하는건 어쩔 수 없는 것 같고요. 

 

"첫 줄은 형편없이 시작되었다"고 저자는 말하고 있는데요, 아니예요.  나름 신선하고 재미있었어요. :-)

 

 

서울에서 태어나서, 고등학교 선생님인 아버지를 따라서 부산으로, 그리고 아버지의 꿈을 쫒아 온 가족이 전라북도 무주로 이사간 후의 삶에 대해서 저자는 "누군가는 추억이라고 쓰고 나는 그걸 지옥이라고 읽지"라면서 10대의 삶이 너무나 비참했노라고 말하고 있어요. 이 수필집을 이소호 시인의 아버지는 읽어보셨으려나 문득 궁금하기도 하고 걱정도 좀 되더라고요. 읽는 독자로서 저는 왠지모를 관음증(?)이 도져서인지, 아니면 내면의 의식의 흐름을 쫓아 술술 읽히는 소설책의 한 장, 한 장을 읽듯이 재미있게, 때론 키득거리면서 읽었거든요. 

 

무주에서의 무료한 삶이 싫었기에 글을 썼고, 무주를 탈출해서 서울로 기어코 가기 위해 대학 진학을 꿈꾸게 했던 만큼 인생에 있어서 쓸모없는 것이란 없는 것인가봐요. 그게 비록 "추억"은 아니었고 "지옥"이었을 망정이라도요. 

 

엄마에 대해서는 절대로 닮고 싶지 않지만 자신도 모르게 닮아있는 사람으로 소개를 하고 있어요.  "솔직한 일기 쓰기"의 중요성과 가치에 대해서 처음으로 가르쳐주셨던 분이 엄마예요. 아내로서, 어머니로서 정체성에 맞추어 살다보니 좋아하던 연극배우의 커리어를 중단해야 했던 어머니. 딸 서울 전세금을 대주었다는 이유로 연락도 없이 불쑥 서울로 찾아오고 잔소리하시는 어머니.... 이런걸 애증의 관계라고 해야 하나요. 이 부분을 읽는데 제 어머니, 아내, 딸이 오버랩되면서 '이 나라, 이 시대에 여자로 산다는 것은 어떤 것일까?' 되묻게 되더군요. 마음이 좀 무거웠어요. 

 

연년생 동생과의 에피소드는 생경한 욕설이 날아다니는 장면마다 저는 키득거리면서 읽었어요.  ㅋㅋ  다 큰 자매 둘이서 남미 여행갔다가 욕하면서 머래채 잡고 싸우는 모습이 그려지는데요, 시인도 욕을 찰지게 하는 구나 싶어서 웃겼어요. 그리고 이런 얘기를 책에 쓸 수도 있구나 싶어서 놀라기도 했구요.  이 수필집을 읽다보니 한 부모에게서 태어났어도 "참 다르구나" 싶고, 우리 사람은 모두 "전지적 자기 중심 시점"에서 보고 듣고 느끼고 생각할 수 밖에 없는 한계가 뚜렷한 존재구나 싶어요. 

 

회사 생활에 대해서도 적나라하게 "꼰대"와 "갑질"에 대해서 까놓았어요. 아직도 저런 회사가 있을 까 싶게 회사가 좀 심하긴 하던데요, "자발적 왕따"를 당하면서 다른 회사 직원들과 어울리지 않았던 저자도 그닥 회사생활을 하기에는 적합한 사회적 인간은 아니었던거 같아요. (요즘같은 코로나 시대에는 점심 혼자 먹는게 그리 이상하지 않으니 이소호 시인같은 자발적 외톨이가 더이상 비주류는 아닐 수도 있겠네요.)  회사 직원 대상으로 하는 서베이에 회식이랑 야근 관련된 꼰대질 그만하라는 글을 "솔직 담백하게" 썼다가 회사를 발칵 뒤집어 놓았다는 장면에서는 왠지 모를 통쾌함을 느꼈다가도, 아무런 송별회도 없이 회사를 그만두었다는 부분에서는 왠지 외로워보였어요. 똘끼와 광기, 자유로운 영혼의 시인이 회사생활을 병행하기에는 아무래도 무리가 있었던 것일까요? "시키는 대로 제멋대로" 의 제목처럼 제멋대로 자기 목소리를 내면서 살아야 하는게 시인이기에요. 시인으로서 다달이 날아오는 카드값 고지서에 힘들어하는 모습도 보여주는 걸 보면, 참, 세상에서 온전히 독립해서 자기 앞가림하면서 제멋대로 산다는게 쉬운일이 아니예요. "시키는 대로"의 삶을 떠나서 "제멋대로"의 삶을 사는데는 용기가 필요하겠지요?  그쵸? 

 

외할아버지에 대해 회고하는 부분은 읽는 내내 마음이 푸근하고 따뜻하고 애뜻했어요. 외할아버지가 체크남방을 즐겨입으시고, 해외 여러나라를 여행하신 멋쟁이시더라구요. 외손녀들에게도 차별없이 골고루 사랑을 나누어주시고, 몰래 특별 용돈도 챙겨주시면서 그걸 낙으로 삼으셨던 분이셨어요. 외할아버지의 임종에 이소호 시인이 펑펑 울었다는 부분에서 사랑하는 이를 떠나보내는 이의 슬픔, 애잔함, 그리움이 고스란히 전해졌어요. 

 

에세이는 정말 얼마만에 읽는지 모르겠어요. 그동안 참 팍팍하고 삭막하게 살았던거 같은데요, 모처럼 재미있는 에세이 읽을 수 있어서 좋았어요. 구글링해서 저자에 대한 기사랑 사진도 좀 찾아봤어요.2018년에 시집 "캣콜링"으로 제 37회 김수영 문학상을 수상하셨던데요, 나중에 이소호 "시인"의 시집도 읽어봐야 겠어요. 

728x90
반응형
Posted by Rfriend
,

산점도 (Scatter plot) 가 두 변수 간의 관계를 2차원(2D으로 시각화해서 볼 수 있는 그래프라면, 버블 그래프(Bubble chart)는 산점도에 버블의 크기와 색깔을 추가하여 3차원(3D) 또는 4차원(4D)의 정보를 2차원에 시각화해서 볼 수 있는 그래프입니다. 그리고 시간 축을 추가하여 애니메이션 형태의 버블 그래프를 그린다면 5차원(5D)의 시각화도 가능합니다. 

 

이번 포스팅에서는 Python의 matplotlib, seaborn, plotly, bubbly 등의 시각화 모듈을 이용해서 버블 그래프를 그리는 방법을 소개하겠습니다. 

 

(1) matplotlib 으로 버블 그래프 그리기 (Bubble chart using Matplotlib)

(2) seaborn 으로 버블 그래프 그리기 (Bubble chart using Seaborn)

(3) plotly 로 동적인 버블 그래프 그리기 (Interactive Bubble chart using Plotly)

(4) bubbly 로 시간의 흐름에 따라 변화하는 버블 그래프 그리기 (Animated Bubble chart using bubbly)

 

 

plotly 모듈에 들어있는 Gapminder  Global Indicator 데이터셋을 대상으로 버블 그래프를 그려보겠습니다. x 축에는 gpdPercap, y 축에는 lifeExp 를 사용하겠으며, pop 에 비례해서 버블의 크기를 조절하고, continent 에 따라서 버블의 색깔을 다르게 해보겠습니다. 

 

## importing modules
import matplotlib.pyplot as plt
import seaborn as sns
import plotly.express as px
import pandas as pd
import numpy as np

## getting dataset
df = px.data.gapminder()


df.info()
# <class 'pandas.core.frame.DataFrame'>
# RangeIndex: 1704 entries, 0 to 1703
# Data columns (total 8 columns):
#  #   Column     Non-Null Count  Dtype  
# ---  ------     --------------  -----  
#  0   country    1704 non-null   object 
#  1   continent  1704 non-null   object 
#  2   year       1704 non-null   int64  
#  3   lifeExp    1704 non-null   float64
#  4   pop        1704 non-null   int64  
#  5   gdpPercap  1704 non-null   float64
#  6   iso_alpha  1704 non-null   object 
#  7   iso_num    1704 non-null   int64  
# dtypes: float64(2), int64(3), object(3)
# memory usage: 106.6+ KB


df.head(3)
# 	country	continent	year	lifeExp	pop	gdpPercap	iso_alpha	iso_num
# 0	Afghanistan	Asia	1952	28.801	8425333	779.445314	AFG	4
# 1	Afghanistan	Asia	1957	30.332	9240934	820.853030	AFG	4
# 2	Afghanistan	Asia	1962	31.997	10267083	853.100710	AFG	4


df.year.max() # ==> keep year 2007
# 2007


df.gdpPercap.describe()   # right-skewed => log scaling
# count      1704.000000
# mean       7215.327081
# std        9857.454543
# min         241.165877dd
# 25%        1202.060309
# 50%        3531.846989
# 75%        9325.462346
# max      113523.132900
# Name: gdpPercap, dtype: float64

 

 

(1) matplotlib 으로 버블 그래프 그리기 (Bubble chart using Matplotlib)

 

matplotlib.pyplot.scatter() 메소드를 사용해서 산점도를 그리면서, 버블의 크기(s)와 색깔(c)을 추가로 지정해주면 됩니다. continent 별로 색깔을 다르게 지정해주었는데요, 아래의 범례(plt.legend()) 설정 방법 참고하세요. alpha=0.5 로 버블이 겹쳤을 때 파악할 수 있도록 투명도를 설정해주었습니다. 

 

x축의 gdpPercap 변수가 왼쪽으로 값이 많이 쏠려있는 형태를 띠고 있으므로 plt.xscale('log') 을 사용해서 x축을 로그 변환 (log transformation) 해주었습니다. plt.grid() 로 점선을 사용해서 그리드 선을 추가해주었습니다. 

 

## (1) Bubble chart with Matplotlib
fig = plt.figure(figsize=(16, 9))
df_2007 = df[df['year']==2007]

ax = plt.scatter(
    x = df_2007['gdpPercap'], 
    y = df_2007['lifeExp'], 
    s = df_2007['pop']/300000, 
    c = pd.Categorical(df_2007['continent']).codes, 
    cmap = "Accent", 
    alpha = 0.5, 
    edgecolors = "gray", 
    linewidth = 1);

plt.xscale('log') # log scaling
plt.xlabel("GDP per Capita")
plt.ylabel("Life Expectancy")
plt.title('Gapminder Global Indicators with Matplotlib', fontsize=16)
plt.grid(True, which='major', linestyle='--', linewidth=0.5)
## setting legend using colors in matplotlib scatter plot
plt.legend(
    handles=ax.legend_elements()[0], 
    labels=['Asia', 'Europe', 'Africa', 'Americas', 'Oceania'])

plt.show()

bubble chart using matplotlib

 

 

 

(2) seaborn 으로 버블 그래프 그리기 (Bubble chart using Seaborn)

 

위의 matplotlib 대비 seaborn.scatter() 메소드는 x축, y축, size, hue(그룹별 색깔 구분) 을 각각 지정해줄 수 있어서 비슷한데요, legend=True 로 지정해주면 size 와 hue (그룹별 색깔 구분) 에 대한 범례를 자동으로 추가해주어서 편리합니다. 

 

## (2) Bubble chart with Seaborn
fig = plt.figure(figsize=(16, 9))
df_2007 = df[df['year']==2007]

sns.scatterplot(
    data = df_2007, 
    x = "gdpPercap", 
    y = "lifeExp", 
    size = "pop", 
    hue = "continent",
    legend = True, 
    sizes = (20, 2000) ,
    alpha=0.5
)

plt.xscale('log') # log scaling
plt.xlabel("GDP per Capita")
plt.ylabel("Life Expectancy")
plt.title('Gapminder Global Indicators with Seaborn', fontsize=16)
plt.grid(True, which='major', linestyle='--', linewidth=0.5)
plt.show()

bubble chart using seaborn

 

 

 

(3) plotly 로 동적인 버블 그래프 그리기 (Interactive Bubble chart using Plotly)

 

plotly 는 위의 matplotlib, seaborn 대비 사용자가 커서를 사용해서 동적으로 버블 그래프(interactive bubble chart)를 조회할 수 있다는 점입니다. hover_name="country" 를 지정해주면 버블 위에 커서를 가져다 놓을 경우 국가 이름과 함께 x축, y축, 버블 크기, 색깔 지정 정보를 팝업 메시지로 보여줍니다. 그리고 커서로 블록을 설정해주면 해당 블록에 대해 줌인(zoom in) 해서 확대해주는 기능도 있습니다. 그 외에 첫번째 줄의 df.query("year==2007") 처럼 px.scatter() 함수 안에서 데이터의 subset 을 query 해서 쓸 수 있으며, log_x = True 를 설정하면 x축의 값을 로그 변환하도록 설정해줄 수도 있습니다. 코드가 매우 깔끔하고 그래프가 이쁜데다 동적이기까지 해서 가독성이 매우 뛰어나기 때문에 왠만하면 matplotlib 이나 seaborn 대신에 plotly 를 사용하는 것이 사용자 입장에서는 좋을 것 같아요. 

 

## (3) Bubble chart with Plotly
import plotly.express as px

fig = px.scatter(
    df.query("year==2007"), 
    x="gdpPercap", 
    y="lifeExp",
    size="pop", 
    color="continent",
    hover_name="country", 
    log_x=True, 
    title='Gapminder Global Indicators with Plotly',
    size_max=60,
    height=650)

fig.show()

 

아래의 Plotly 로 그린 버블 그래프의 버블 위에 커서를 가져가면 뜨는 캡션 정보와, 커서로 블록을 선정해서 hover 해서 zoom in 하는 모습을 화면 녹화한 것이예요. 코드 몇줄로 이게 된다는게 신기하지요?! plotly 짱! ^^

 

 

 

 

(4) bubbly 로 시간의 흐름에 따라 변화하는 버블 그래프 그리기 (Animated Bubble chart using bubbly)

 

이번에는 시간 축을 추가 (adding timestamp axis)해서 시간의 흐름에 따라서 버블 그래프가 변화하는 애니메이션을 시각화 해보겠습니다. bubbly 모듈의 bubbleplot() 메소드를 사용하며, time_column='year' 에 년도(year)를 시간 축으로 설정해주었습니다. x축은 x_column, y축은 y_column, 버블 크기는 size_column, 버블 색깔은 color_column 로 설정해주면 됩니다. x_logscale=True 로 x축 값을 로그 변환해주었으며, scale_bubble=3 으로 버블의 스케일을 지정해주었습니다. 

 

#!pip install bubbly

## (4) Animated Bubble Chart
## Using the function bubbleplot from the module bubbly(bubble charts with plotly)
## ref: https://github.com/AashitaK/bubbly

from bubbly.bubbly import bubbleplot 
from __future__ import division
from plotly.offline import init_notebook_mode, iplot
init_notebook_mode()

figure = bubbleplot(
    dataset=df, 
    x_column='gdpPercap', 
    y_column='lifeExp', 
    bubble_column='country', 
    time_column='year', 
    size_column='pop', 
    color_column='continent', 
    x_title="GDP per Capita", 
    y_title="Life Expectancy", 
    title='Animated Gapminder Global Indicators with Bubbly',
    x_logscale=True, 
    scale_bubble=3, 
    height=650)

iplot(figure, config={'scrollzoom': True})

 

 

아래는 애니메이션 버블 차트를 실행시킨 모습을 화면 녹화한 것이예요. 왼쪽 하단의 'Play' 버튼을 누르면 애니메이션이 실행이 되면서 년도의 순서대로 버블 차트가 변화는 것을 볼 수 있습니다. 신기하지요?! ^^ bubbly 짱!! (bubbly 의 백본이 plotly 이므로 역시 plotly 짱!!)

 

 

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요!  :-)

 

728x90
반응형
Posted by Rfriend
,

Sankey Diagram 은 화살표 너비가 흐름 속도에 비례(the width of the arrows is proportional to the flow rate)하는 흐름 다이어그램의 한 유형(a type of flow diagram)입니다. Sankey Diagram은 또한 에너지 계정, 지역 또는 국가 차원의 자재 흐름 계정 및 비용 분석을 시각화할 수 있습니다. 다이어그램은 종종 재료 흐름 분석의 시각화에 사용됩니다. 


Sankey Diagram은 시스템 내의 주요 전송 또는 흐름(major transfers or flows)을 강조합니다. 그들은 흐름에 대한 가장 중요한 기여의 위치를 찾는데 도움을 줍니다. 그것들은 종종 정의된 시스템 경계 내에서 보존된 양을 보여줍니다. [1]

 

 

Python에서 Sankey Diagram 을 그리는 모듈로 HoloViews 와 plotly 가 있는데요, HoloViews 가 구문이 좀더 직관적이어서 이해하기가 쉽습니다. 

 

이번 포스팅에서는 Python의 HoloViews 모듈을 사용해서 Jupyter Notebook에서 Sankey Diagram 을 시각화하는 방법을 소개하겠습니다. [2]

 

HoloViews 모듈은 백본으로 Matplotlib 과 Bokeh 를 사용하므로, 만약 이들 모듈이 설치되어 있지 않다면 pip install 로 먼저 이들 모듈을 설치해주어야 합니다. 

 

 

## in a command line

$ pip install bokeh
$ pip install holoviews

 

Sankey elements represent flows and their quantities in proportion to one another. The data of a Sankey element defines a directed, acyclic graph, making it a specialized subclass of the Graph element. The width of the lines in a Sankey diagram represent the magnitudes of each edge. Both the edges and nodes can be defined through any valid tabular format including pandas dataframes, dictionaries/tuples of columns, NumPy arrays and lists of tuples.

The easiest way to define a Sankey element is to define a list of edges and their associated quantities:

 

Sankey 요소는 흐름과 흐름의 양을 서로 비례하여 나타냅니다. Sankey 요소의 데이터는 방향성 있는 비순환 그래프(directed, acyclic graph)를 정의하여 그래프 요소의 특수 하위 클래스로 만듭니다. Sankey Daigram 에서 선의 너비는 각 Edge의 크기를 나타냅니다. Edge와 Node 모두 pandas DataFrame, Dictionaries/Tuples of columns, NumPy Array 및 Lists of Tuples을 포함한 유효한 표 형식을 통해 정의할 수 있습니다. 이게 바로 Holoviews 가 편리하고 직관적인 이유예요. 

 

Sankey 요소를 정의하는 가장 쉬운 방법은 list of edges과 관련 수량을 정의하는 것입니다. 아래 예에서는 A, B node에서 X, Y, Z node 로의 흐름의 관계와 양을 리스트 형식으로 정의한 것입니다. [2]

 

import holoviews as hv
from holoviews import opts, dim
hv.extension('bokeh')

# defining a list of edges and their associated quantities
sankey = hv.Sankey([
    ['A', 'X', 10],
    ['A', 'Y', 20],
    ['A', 'Z', 30],
    ['B', 'X', 60],
    ['B', 'Y', 5],
    ['B', 'Z', 40]]
)

sankey.opts(width=800, height=500)

sankey diagram using holoviews module in python

 

 

 

아래의 예시는 2010년 왕립학회 정책 보고서 "The Scientific Century: securing our future prosperity"에 설명된 영국 박사과정 학생들의 진로에 대한 간단한 데이터 세트를 Sankey Diagram으로 그려본 것입니다. 우리는 정수 지수로 열거된 노드와 각 경력 단계 사이에 흐르는 백분율을 정의합니다. 마지막으로 "To"라는 레이블을 붙이는 대상 노드에 의해 값과 색상에 대한 단위를 가진 차원을 정의합니다. [2]

 

위의 예에서는 nodes의 이름을 그대로 사용하였다면, 아래의 예에서는 nodes에 대해서 enumerate() 함수를 사용해서 index 정수를 매핑해서 holoveiws Dataset 객체로 만들어 주고, 이를 edges 의 관계를 정의할 때 index 정수를 사용하였습니다. 

 

nodes = ["PhD", "Career Outside Science",  "Early Career Researcher", "Research Staff",
         "Permanent Research Staff",  "Professor",  "Non-Academic Research"]
for i, j in enumerate(nodes):
    print(i, ':', j)
    
# 0 : PhD
# 1 : Career Outside Science
# 2 : Early Career Researcher
# 3 : Research Staff
# 4 : Permanent Research Staff
# 5 : Professor
# 6 : Non-Academic Research

 

 

Label의 위치를 오른쪽으로 바꾸어 주었으며 (label_position='right'), cmap, edge_color, node_color 로 edge와 node 의 색깔도 다르게 설정해주었습니다. 색깔이 다르니 가독성이 한결 좋아졌습니다. 

 

## the edges are expressed as integer node indexes and labels are provided separately.
## We can explicitly define the set of nodes as a Dataset of indexes and labels as key 
## and value dimensions respectively.
nodes = ["PhD", "Career Outside Science",  "Early Career Researcher", "Research Staff",
         "Permanent Research Staff",  "Professor",  "Non-Academic Research"]
nodes = hv.Dataset(enumerate(nodes), 'index', 'label')

edges = [
    (0, 1, 53), 
    (0, 2, 47), 
    (2, 6, 17), 
    (2, 3, 30), 
    (3, 1, 22.5), 
    (3, 4, 3.5), 
    (3, 6, 4.), 
    (4, 5, 0.45)   
]

value_dim = hv.Dimension('Percentage', unit='%')
careers = hv.Sankey((edges, nodes), ['From', 'To'], vdims=value_dim)

careers.opts(
    opts.Sankey(labels='label', 
                label_position='right', # adjust the label_position from "right" to "left"
                width=900, height=500, 
                cmap='Set1',
                edge_color=dim('To').str(), # setting edge color
                node_color=dim('index').str()) # setting node color
)

sankey diagram using holoviews in python

 

 

 

Interactive Diagram 이어서 커서를 가져다대면 아래의 화면 캡쳐와 같이 해당 Edge 가 하이라이드 되며 해당 Edge의 From note, To note, Percentage 등의 캡션 정보를 팝업으로 볼 수 있습니다.  (아래는 화면 캡쳐이므로 Interactive 기능 없음. Jupyter Notebook에서 실행해야지 Interactive 기능 사용 가능함)

 

 

[ Reference ]

 

[1] Introduction to Sankey Diagram: https://en.wikipedia.org/wiki/Sankey_diagram

[2] Sankey Element using HoloViews: https://holoviews.org/reference/elements/bokeh/Sankey.html

[3] R로 Sankey Diagram 시각화 하기: https://rfriend.tistory.com/220

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요!  :-)

 

728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 PostreGQL, Greenplum DB에서 SQL 함수를 사용하여 문자열의 일부분을 가져오는 두가지 함수를 비교하여 소개하겠습니다. 

 

(1) 위치 기반(position based)으로 문자열의 일부분 가져오기: SUBSTRING(), SUBSTR()

(2) 구분자를 기반(delimiter based)으로 문자열을 분할하여 일부분 가져오기: SPLIT_PART()

 

PostgreSQL, Greenplum: SUBSTR(), SPLIT_PART()

 

SUBSTR() 함수는 문자열의 포맷이 일정하게 정해져 있어서 위치를 기반으로 문자열의 특정한 일부분만 가져올 때 사용합니다. 반면에, SPLIT_PART() 함수는 문자열에 구분자(delimiter)가 있어서, 이 구분자를 기준으로 문자열을 구분한 후에 특정 순서에 위치한 문자열의 일부분을 가져올 때 사용합니다. 

 

아래에 간단한 예를 들어서 설명하겠습니다. 

 

 

(1) 위치 기반(position based)으로 문자열의 일부분 가져오기: SUBSTRING(), SUBSTR()

 

- syntax: SUBSTR(문자열, 시작 위치, 가져올 문자 개수)

 

substr() 함수와 substring() 함수는 동일합니다. 

 

---------------------------------------------
-- String functions in PostgreSQL
-- substr() vs. split_part()
---------------------------------------------

-- (1) substr(string, from [, count])
-- : Extract substring
-- : when position is fixed

SELECT 
	SUBSTR('abc_def_01', 1, 3) AS substr_1
	, SUBSTR('abc_def_01', 5, 3) AS substr_2
	, SUBSTR('abc_def_01', 9, 2) AS substr_3;

--substr_1|substr_2|substr_3|
----------+--------+--------+
--abc     |def     |01      |


-- or equivalently  (same as substring(string from from for count))
SELECT 
	SUBSTRING('abc_def_01', 1, 3) AS substr_1
	, SUBSTRING('abc_def_01', 5, 3) AS substr_2
	, SUBSTRING('abc_def_01', 9, 2) AS substr_3;

 

 

 

 

(2) 구분자를 기반(delimiter based)으로 문자열을 분할하여 일부분 가져오기: SPLIT_PART()

 

- syntax: SPLIT_PART(문자열, 구분자 텍스트, 가져올 필드 순서)

 

-- (2) split_part(string text, delimiter text, field int)
-- : Split string on delimiter and return the given field (counting from one)
-- : when deliiter is fixed

SELECT 
	SPLIT_PART('abc_def_01', '_', 1) AS split_part_1
	, SPLIT_PART('abc_def_01', '_', 2) AS split_part_2
	, SPLIT_PART('abc_def_01', '_', 3) AS split_part_3;

--split_part_1|split_part_2|split_part_3|
--------------+------------+------------+
--abc         |def         |01          |

 

 

[ Reference ]

* PostgreSQL string functions and operators
: https://www.postgresql.org/docs/9.1/functions-string.html

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요!

 

728x90
반응형
Posted by Rfriend
,

데이터 유형별로 여러 단계를 거치는 데이터 전처리와 모델의 학습, 신규 데이터에 대한 예측의 전체 기계학습 워크 플로우를 파이프라인으로 관리하면 워크 플로우 관리를 간소화하고 자동화(workflow automation) 하는데 매우 큰 도움이 됩니다. 

 

이번 포스팅에서는 Python의 scikit learn 모듈을 사용해서 숫자형과 범주형 변수가 섞여 있는 데이터셋의 데이터 전처리 및 선형회귀모형 모델을 학습하는 전체 파이프라인을 만드는 방법을 소개하겠습니다. 

 

(1) 숫자형 변수의 결측값 처리 및 표준화하는 데이터 전처리 파이프라인 만들기

(2) 범주형 변수의 원핫인코딩하는 데이터 전처리 파이프라인 만들기

(3) ColumnTransformer 클래스로 숫자형과 범주형 변수 전처리 파이프라인 합치기

(4) 숫자형 & 범주형 데이터 전처리와 선형회귀모형 학습하는 파이프라인 만들기

(5) 모델 학습

(6) 예측 및 모델 성능 평가

 

scikit-learn pipeline with numeric and categorical features and linear regression

 

 

먼저, 데이터 전처리 및 모델학습과 파이프라인을 구성하는데 필요한 Python modules 을 불러오겠습니다.  그리고 예제로 사용할 오픈 abalone.data 데이터셋을 가지고 pandas DataFrame을 만들어보겠습니다. 

 

## Importing modules
import numpy as np
import pandas as pd
from sklearn.compose import ColumnTransformer
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler
from sklearn.preprocessing import OneHotEncoder
from sklearn.linear_model import LinearRegression
from sklearn.pipeline import Pipeline
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error


## Making a DataFrame by reading abalone data set from URL
url = "http://archive.ics.uci.edu/ml/machine-learning-databases/abalone/abalone.data"
abalone = pd.read_csv(
    url, 
    sep=",", 
    names = ['sex', 'length', 'diameter', 'height', 
             'whole_weight', 'shucked_weight', 'viscera_weight', 
             'shell_weight', 'rings'], 
    header = None)
    
    
abalone.head()
# 	sex	length	diameter	height	whole_weight	shucked_weight	viscera_weight	shell_weight	rings
# 0	M	0.455	0.365	0.095	0.5140	0.2245	0.1010	0.150	15
# 1	M	0.350	0.265	0.090	0.2255	0.0995	0.0485	0.070	7
# 2	F	0.530	0.420	0.135	0.6770	0.2565	0.1415	0.210	9
# 3	M	0.440	0.365	0.125	0.5160	0.2155	0.1140	0.155	10
# 4	I	0.330	0.255	0.080	0.2050	0.0895	0.0395	0.055	7

 

 

위의 abalone 데이터셋에서 숫자형 변수(numeric variable)인 "length", "whole_weight" 와 범주형 변수(categorical variable)인 "sex" 의 3개 칼럼을 이용해서 "rings" 를 예측하는 선형회귀모형(linear regression)을 학습시키고, 예측을 해보겠습니다. 

 

 

(1) 숫자형 변수의 결측값 처리 및 표준화하는 데이터 전처리 파이프라인 만들기

 

숫자형 변수(numeric features)에 대해서는

  (1-1) scikit learn 모듈의 SimpleImputer() 클래스를 이용해서 결측값을 "중앙값(median)"으로 대체(imputation) 

  (1-2) scikit learn 모듈의 StandardScaler() 클래스를 이용해서 표준화(standardization) 

하는 단계를 거치는 파이프라인을 만들어보겠습니다. 

 

콤마로 써주는 부분은 각 단계(step)의 alias 이름이 되겠습니다.  아래의 (5)번에서 전체 파이프라인을 시각화했을 때 콤마로 부연설명해준 alias 이름을 도식화한 파이프라인의 윗부분에서 볼 수 있습니다. 

 

## (1) for numeric features
num_features = ["length", "whole_weight"]
num_transformer = Pipeline(
    steps = [("imputer", SimpleImputer(strategy="median")), ("scaler", StandardScaler())]
)

 

 

 

(2) 범주형 변수의 원핫인코딩하는 데이터 전처리 파이프라인 만들기

 

범주형 변수(categorical features)에 대해서는 scikit learn 모듈의 OneHotEncoder() 클래스를 사용해서 각 범주별로 칼럼을 만들어서 해당 범주에 속하면 '1' (hot), 해당 범주에 속하지 않으면 '0' (cold) 으로 인코딩을 해서 기계가 인식할 수 있도록 해주는 단계를 정의하겠습니다. 

(참고로, handle_unknown = "ignore" 옵션은 모델을 학습할 때 학습 데이터셋의 범주형 변수에는 없었던 범주가 예측에 사용하는 새로운 데이터셋에서 나타날 경우 무시하라는 의미입니다.)

 

## (2) Categorical features
cat_features = ["sex"]
cat_transformer = OneHotEncoder(handle_unknown="ignore")

 

 

 

(3) ColumnTransformer 클래스로 숫자형과 범주형 변수 전처리 파이프라인 합치기

 

위의 (1) 숫자형 데이터 전처리 단계와 (2) 범주형  데이터 전처리 단계를 정의하는 클래스와 파이프라인을 scikit learn 모듈의 ColumnTransformer() 클래스를 사용해서 숫자형 변수(num_features)와 범주형 변수(cat_features) 별로 매핑하여 하나의 데이터 전처리 파이프라인으로 합쳐보겠습니다. 

 

## (3) Use ColumnTransformer by selecting column by names
preprocessor = ColumnTransformer(
    transformers = [
        ("num", num_transformer, num_features), 
        ("cat", cat_transformer, cat_features)
    ]
)

 

 

 

(4) 숫자형 & 범주형 데이터 전처리와 선형회귀모형 학습하는 파이프라인 만들기

 

이번에는 위의 (3)번에서 숫자형과 범주형 데이터 전처리를 하나로 묶은 데이터 전처리 파이프라인에 (4) 선형회귀모형을 적합하는 클래스를 추가해서 파이프라인을 만들어보겠습니다. 

 

## (4) Append regressor to preprocessing pipeline.
lin_reg = Pipeline(
    steps = [("preporcessor", preprocessor), ("regressor", LinearRegression())]
)

 

 

(5) 모델 학습

 

모델을 학습할 때 사용할 training set (0.8) 과 모델의 성능을 평가할 때 사용할 test set (0.2) 를 0.8:0.2의 비율로 무작위 추출해서 분할해보겠습니다. 

 

## Split training(0.8) and test set(0.2) randomly
X = abalone[["length", "whole_weight", "sex"]]
y = abalone["rings"]

X_train, X_test, y_train, y_test = train_test_split(X, y, 
                                                    test_size=0.2, 
                                                    random_state=1004)

 

 

sklean 의 set_config(display="diagram") 을 설정해주면 위의 (1) ~ (4) 에서 정의해준 숫자형 & 범주형 변수 데이터 전처리와 선형회귀모형 적합의 전체 기계학습 워크플로우 파이프라인을 다이어그램으로 시각화해서 볼 수 있습니다. 

 

참고로, 파이프라인의 각 단계의 박스를 커서로 클릭하면 상세 옵션 설정 내용(예: SimpleImputer 클래스의 경우 strategy='median', OneHotEncoder 클래스의 handle_unknown='ignore' 등) 을 펼쳐서 볼 수 있습니다. 그리고 "num"과 "cat" 부분을 클릭하면 숫자형 변수와 범주형 변수 이름을 확인할 수 있습니다. 

 

이제 드디어 준비가 다 되었군요.  lin_reg.fit(X_train, y_train) 을 실행하면 (1)~(4)의 전체 워크플로우가 파이프라인을 따라서 순차적으로 실행이 됩니다.  코드가 참 깔끔해졌지요! 

 

## Display a diagram of Pipelines in Jupyter Notebook
from sklearn import set_config

set_config(display="diagram")

## Fit a Linear Regression model using pipelines
lin_reg.fit(X_train, y_train)

 

 

 

(6) 예측 및 모델 성능 평가

 

이제 따로 떼어놓았던 test set (0.2) 를 가지고 MAPE (Mean Absolute Percentage Error) 지표를 사용해서 모델의 성능을 평가해보겠습니다. 

 

lin_reg.predict(X_test) 를 실행하면 앞서 정의했던 (1) ~ (4) 의 숫자형 & 문자형 변수별 데이터 전처리와 모델 예측의 전체 워크 플로우의 파이프라인이 물 흐르듯이 자동으로 알아서 진행이 됩니다.  너무나 신기하고 편리하지요?!  

(파이프라인이 있으면 lin_reg.predict(X_test) 라는 코드 단 한줄이면 되는데요, 만약 위의 (1) ~ (4) 의 과정을 수작업으로 Test set 에 대해서 데이터 전처리해주는 코드를 다시 짠다고 생각을 해보세요. -_-;)

 

## Define UDF of MAPE(Mean Absolute Percentage Error)
## or sklearn.metrics.mean_absolute_percentage_error() class, which is new in version 0.24.
## : https://scikit-learn.org/stable/modules/generated/sklearn.metrics.mean_absolute_percentage_error.html#sklearn.metrics.mean_absolute_percentage_error
def MAPE(y_test, y_pred): 
    y_test, y_pred = np.array(y_test), np.array(y_pred) 
    return np.mean(np.abs((y_test - y_pred) / y_test)) * 100
    

## Evaluate performance of a model
y_pred = lin_reg.predict(X_test)

print("MSE: %.2f" % MAPE(y_test, y_pred))
#[out] MSE: 18.68

 

 

[ Reference ]

- Column Transformer with Mixed Types
https://scikit-learn.org/stable/auto_examples/compose/plot_column_transformer_mixed_types.html
- sklearn SimpleImputer
https://scikit-learn.org/stable/modules/generated/sklearn.impute.SimpleImputer.html
- sklearn StandardScaler
https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.StandardScaler.html
- sklearn OneHotEncoder
: https://scikit-learn.org/stable/modules/generated/sklearn.preprocessing.OneHotEncoder.html
- sklearn Linear Regression
https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LinearRegression.html
- sklearn Pipeline
https://scikit-learn.org/stable/modules/generated/sklearn.pipeline.Pipeline.html
- sklearn train_test_split
https://scikit-learn.org/stable/modules/generated/sklearn.model_selection.train_test_split.html
- sklearn mean_squared_error
https://scikit-learn.org/stable/modules/generated/sklearn.metrics.mean_squared_error.html

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요!  :-)

 

 

728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 PostgreSQL, Greenplum 에서 Apahe MADlib 의 함수를 사용하여 

(1) 2D array 를 1D array 로 unnest 하기

    (Unnest 2D array into 1D array in PostgreSQL using madlib.array_unnest_2d_to_1d() function)

(2) 1D array 에서 순서대로 원소 값을 indexing 하기

하는 방법을 소개하겠습니다. 

 

 

how to unnest 2D array into 1D array and indexing in PostgreSQL, Greenplum DB

 

먼저, 예제로 사용할 간단한 2D array를 포함하는 테이블을 만들어 보겠습니다. 

 

--------------------------------------------------------------------------------
-- How to unnest a 2D array into a 1D array in PostgreSQL?
-- [reference] http://madlib.incubator.apache.org/docs/latest/array__ops_8sql__in.html#af057b589f2a2cb1095caa99feaeb3d70
--------------------------------------------------------------------------------

-- Creating a sample 2D array table
DROP TABLE IF EXISTS mat_2d_arr;
CREATE TABLE mat_2d_arr (id int, var_2d int[]);
INSERT INTO mat_2d_arr VALUES 
(1,  '{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}'),
(2,  '{{10, 11, 12}, {13, 14, 15}, {16, 17, 18}}'),
(3,  '{{19, 20, 21}, {22, 23, 24}, {25, 26, 27}}'), 
(4,  '{{28, 29, 30}, {31, 32, 33}, {34, 35, 36}}');


SELECT * FROM mat_2d_arr ORDER BY id;

--id|var_2d                            |
----+----------------------------------+
-- 1|{{1,2,3},{4,5,6},{7,8,9}}         |
-- 2|{{10,11,12},{13,14,15},{16,17,18}}|
-- 3|{{19,20,21},{22,23,24},{25,26,27}}|
-- 4|{{28,29,30},{31,32,33},{34,35,36}}|

 

 

(1) 2D array 를 1D array 로 unnest 하기

    (Unnest 2D array into 1D array in PostgreSQL using madlib.array_unnest_2d_to_1d() function)

 

Apache MADlib 의 madlib.array_unnest_2d_to_1d() 함수를 사용하면 쉽게 PostgreSQL, Greenplum의 2D array를 1D array 로 unnest 할 수 있습니다. madlib.array_unnest_2d_to_1d() 함수는 'unnest_row_id' 와 'unnest_result' 의 2개 칼럼을 반환하므로, 이들 "2개 칼럼 모두"를 반환하라는 의미로 (madlib.array_unnest_2d_to_1d(var_2d)).* 함수의 처음과 끝부분에 괄호 ()로 묶고 마지막에 아스타리스크(.*) 부호를 붙여주었습니다. ().* 를 빼먹지 않도록 주의하세요. 

 

MADlib 함수를 사용하지 않는다면 직접 PL/SQL 사용자 정의 함수나 또는 PL/Python 이나 PL/R 사용자 정의 함수를 정의하고 실행해야 하는데요, 좀 번거롭고 어렵습니다. 

 

-- (1) Unnest 2D array into a 1D array using madlib.array_unnest_2d_to_1d() function
SELECT 
	id
	, (madlib.array_unnest_2d_to_1d(var_2d)).* 
FROM mat_2d_arr 
ORDER BY id, unnest_row_id;

--id|unnest_row_id|unnest_result|
----+-------------+-------------+
-- 1|            1|{1,2,3}      |
-- 1|            2|{4,5,6}      |
-- 1|            3|{7,8,9}      |
-- 2|            1|{10,11,12}   |
-- 2|            2|{13,14,15}   |
-- 2|            3|{16,17,18}   |
-- 3|            1|{19,20,21}   |
-- 3|            2|{22,23,24}   |
-- 3|            3|{25,26,27}   |
-- 4|            1|{28,29,30}   |
-- 4|            2|{31,32,33}   |
-- 4|            3|{34,35,36}   |

 

 

 

아래는 2D array를 1D array로 unnest 하는 사용자 정의함수(UDF) 를 plpgsql 로 정의해서 SQL query 로 호출해서 사용하는 방법입니다. 

 

-- UDF for unnest 2D array into 1D array
CREATE OR REPLACE FUNCTION unnest_2d_1d(ANYARRAY, OUT a ANYARRAY)
  RETURNS SETOF ANYARRAY
  LANGUAGE plpgsql IMMUTABLE STRICT AS
$func$
BEGIN
   FOREACH a SLICE 1 IN ARRAY $1 LOOP
      RETURN NEXT;
   END LOOP;
END
$func$;



-- Unnest 2D array into 1D array using the UDF above
SELECT 
	id 
	, unnest_2d_1d(var_2d) AS var_1d
FROM mat_2d_arr
ORDER BY 1, 2;

--id|var_1d    |
----+----------+
-- 1|{1,2,3}   |
-- 1|{4,5,6}   |
-- 1|{7,8,9}   |
-- 2|{10,11,12}|
-- 2|{13,14,15}|
-- 2|{16,17,18}|
-- 3|{19,20,21}|
-- 3|{22,23,24}|
-- 3|{25,26,27}|
-- 4|{28,29,30}|
-- 4|{31,32,33}|
-- 4|{34,35,36}|

 

 

 

 

(2) 1D array 에서 순서대로 원소 값을 indexing 하기

 

일단 2D array를 1D array 로 unnest 하고 나면, 그 다음에 1D array에서 순서대로 각 원소를 inndexing 해오는 것은 기본 SQL 구문을 사용하면 됩니다. 1D array 안에 각 3개의 원소들이 들어 있으므로, 순서대로 unnest_result[1], unnest_result[2], unnest_result[3] 으로 해서 indexing 을 해오면 아래 예제와 같습니다. 

 

-- (2) Indexing an unnested 1D array
SELECT 
	a.id 
	, unnest_row_id
	, unnest_result[1] AS x1
	, unnest_result[2] AS x2
	, unnest_result[3] AS x3
FROM (
	SELECT 
		id
		, (madlib.array_unnest_2d_to_1d(var_2d)).* 
	FROM mat_2d_arr 
) AS a
ORDER BY id, unnest_row_id;

--id|unnest_row_id|x1|x2|x3|
----+-------------+--+--+--+
-- 1|            1| 1| 2| 3|
-- 1|            2| 4| 5| 6|
-- 1|            3| 7| 8| 9|
-- 2|            1|10|11|12|
-- 2|            2|13|14|15|
-- 2|            3|16|17|18|
-- 3|            1|19|20|21|
-- 3|            2|22|23|24|
-- 3|            3|25|26|27|
-- 4|            1|28|29|30|
-- 4|            2|31|32|33|
-- 4|            3|34|35|36|

 

 

 

[Reference]

- Apache MADlib's madlib.array_unnest_2d_to_1d() function: http://madlib.incubator.apache.org/docs/latest/array__ops_8sql__in.html#af057b589f2a2cb1095caa99feaeb3d70

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요!  :-)

 

728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 Python의 numpy 2D 행렬에서 행 기준, 열기준 백분율을 구하여 히트맵을 그리는 방법을 소개하겠습니다. 

 

(1) numpy 2D array 의 행 기준 백분율을 구해서 히트맵 그리기

     (Plotting the heatmap of percentages by row in numpy 2D array)

(2) numpy 2D array 의 열 기준 백분율을 구해서 히트맵 그리기 

     (Plotting the heatmap of percentages by column in numpy 2D array)

 

 

먼저, 예제로 사용할 numpy 2D array 를 0~4 사이의 정수 난수를 생성해서 만들어보겠습니다. 

 

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

## creating a sample dataset of numpy 2D array
np.random.seed(1004)
mat = np.random.randint(low=0, high=5, size=(5, 5))

print(mat)
# [[2 3 3 4 1]
#  [2 0 0 4 4]
#  [0 2 4 2 3]
#  [3 0 4 3 0]
#  [0 2 4 1 4]]

 

 

(1) numpy 2D array 의 행 기준 백분율을 구해서 히트맵 그리기

     (Plotting the heatmap of percentages by row in numpy 2D array)

 

 

numpy 2D array 에서 행 기준 백분율을 구하기 위해서는 행 기준의 합(summation by row, sum(axis=1, keepdims=True)) 이 필요합니다.  

 

이때 mat.sum(axis=1, keepdims=True) 처럼 keepdims=True 옵션을 표기해줌으로써 shape(5, 1) 의 각 행별 합계를 구할 수 있습니다. 만약 keepdims 옵션을 명기하지 않을 경우 디폴트로서 keepdims=False (by default) 가 적용이 되어서 shape(5,)  의 array([13, 10, 11, 10, 11]) 의 계산 결과가 나오며, 이걸로 나누어 주면 행 기준 백분율이 아니라 element-wise 나눗셈이 되어서 전혀 엉뚱한 계산결과가 나오므로 주의가 필요합니다. 

 

## (1) plotting heatmap of numpy percentages along axis 1 in 2D array

## row sums in matrix using 'keepdims=True' option
row_sum = mat.sum(axis=1, keepdims=True)

print(row_sum)
# [[13]
#  [10]
#  [11]
#  [10]
#  [11]]


## when 'keepdims=False' (by default)
mat.sum(axis=1)
# array([13, 10, 11, 10, 11])

 

 

 

위에서 구한 shape (5, 1) 의 행 기준 합계  row_sum 행렬로 원래의 2D array 행렬 mat  을 나누어주면, numpy 2D array에서 행 기준의 백분율을 구할 수 있습니다. np.round(x, decimals=2) 함수를 사용해서 백분율 계산 결과를 소수점 2째 자리까지만 나오도록 반올림을 해주었습니다. 

 

## calculating row percentage in matrix
mat_row_pct = mat / row_sum

## Round array elements to the given number of decimals
mat_row_pct = np.round(mat_row_pct, decimals=2)

print(mat_row_pct)
# [[0.15 0.23 0.23 0.31 0.08]
#  [0.2  0.   0.   0.4  0.4 ]
#  [0.   0.18 0.36 0.18 0.27]
#  [0.3  0.   0.4  0.3  0.  ]
#  [0.   0.18 0.36 0.09 0.36]]

 

 

 

이제 matplotlib 의 matshow() 함수와 colormap 을 사용해서, 위에서 구한 행 기준 백분율에 대해 히트맵을 그려보겠습니다. colormap 으로는 순차적으로 파란색이 진해지(sequential colormaps)는 camp='Blues' 를 사용하였습니다. 그리고 해석을 편리하게 할 수 있도록 plt.colorbar() 함수를 사용해서 칼라의 강도별 백분율을 legend colorbar 를 만들어주었습니다. 아래에 히트맵을 보면 한눈에 행 기준의 백분율이 어디 셀이 높고 어디 셀이 낮은지 한눈에 금방 알아볼 수 있습니다. 

 

## plotting the heatmap of matrix row percentage (axis=1) using colormaps
## ref: https://matplotlib.org/stable/tutorials/colors/colormaps.html

plt.rcParams['figure.figsize'] = (8, 8) # figure size
plt.matshow(mat_row_pct, cmap='Blues') # sequential colormaps
plt.title('Heatmap of row percentage in matrix', fontsize=16)
plt.colorbar(label='percentages along axis 1') # colorbar legend
plt.show()

heatmap of row percentage in numpy 2D array

 

 

 

 

(2) numpy 2D array 의 열 기준 백분율을 구해서 히트맵 그리기 

     (Plotting the heatmap of percentages by column in numpy 2D array)

 

이번에는 mat.sum(axis=0, keepdims=True)  로 numpy 2D array에서 열 기준으로 백분율(percentages by column in numpy 2D array)을 구해보겠습니다. 앞에서와는 다르게 열 기준 합을 구할 때는 axis = 0 을 사용하였습니다. 

(열 기준 백분율을 구할 때는 keepdims=False 로 해도 결과는 동일합니다.)

 

## (2) plotting heatmap of numpy percentages along axis 0 in 2D array
## row sums in matrix
print(mat)
# [[2 3 3 4 1]
#  [2 0 0 4 4]
#  [0 2 4 2 3]
#  [3 0 4 3 0]
#  [0 2 4 1 4]]

col_sum = mat.sum(axis=0, keepdims=True)

print(col_sum)
# [[ 7  7 15 14 12]]

 

 

 

이제 위에서 구한 열 기준 합으로 원래의 2D array 를 나누어주어서 열 기준 백분율을 구하겠습니다. 그리고 np.round(array, decimals=2) 함수를 사용해서 백분율 행렬의 원소 값을 소수점 2째 자리까지만 나오도록 반올림 해보겠습니다. 

 

## calculating row percentage in matrix
mat_col_pct = mat / col_sum

## Round array elements to the given number of decimals
mat_col_pct = np.round(mat_col_pct, decimals=2)

print(mat_col_pct)
# [[0.29 0.43 0.2  0.29 0.08]
#  [0.29 0.   0.   0.29 0.33]
#  [0.   0.29 0.27 0.14 0.25]
#  [0.43 0.   0.27 0.21 0.  ]
#  [0.   0.29 0.27 0.07 0.33]]

 

 

 

2D array 에서 열 기준 백분율이 준비가 되었으니 matplotlib의 plt.matshow() 함수에 순차적으로 빨간색의 강도가 진해지는 colormap 으로서 cmap='Reds' 를 사용하여 히트맵을 그려보겠습니다. 

 

## plotting the heatmap of matrix column percentage (axis=0) using colormaps
## ref: https://matplotlib.org/stable/tutorials/colors/colormaps.html
plt.rcParams['figure.figsize'] = (8, 8)
plt.matshow(mat_col_pct, cmap='Reds') # sequential colormaps
plt.title('Heatmap of row percentage in matrix', fontsize=16)
plt.colorbar(label='percentages along axis 0') # colorbar legend
plt.show()

colormap of percentages by column in numpy 2D array

 

 

[Reference]

* choosing colormaps in matplotlib: https://matplotlib.org/stable/tutorials/colors/colormaps.html

 

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요!  :-)

 

728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 R data.frame에서 여러개의 칼럼 이름을 '변경 전 칼럼 이름 : 변경 후 칼럼 이름'의 매핑 테이블 (old_column_name : new_column_name mapping table) 을 이용해서 한꺼번에 변경하는 방법을 소개하겠습니다. data.frame에 칼럼 개수가 엄청 많고, 특정 칼럼에 대해서 선별적으로 칼럼 이름을 변경하고 싶을 때 전:후 칼럼 이름 매핑 테이블을 사용하는 이번 포스팅의 방법을 사용하면 편리합니다. 

 

renaming column names using mapping table in R data.frame

 

 

(1) 모든 칼럼을 순서대로 칼럼 이름을 변경하고 싶은 경우

 

참고로, R 에서 names(), rename() 등의 함수를 이용해서 칼럼 이름을 변경하는 방법은 https://rfriend.tistory.com/41 를 참고하세요. 

 

 

먼저, "X1" ~ "X10" 까지의 10개 칼럼을 가지는 예제 data.frame 을 만들어보겠습니다. 

 

## -- creating a sample data.frame with 10 columns
df <- data.frame(matrix(1:30, nrow=3))

print(df)
# X1 X2 X3 X4 X5 X6 X7 X8 X9 X10
# 1  1  4  7 10 13 16 19 22 25  28
# 2  2  5  8 11 14 17 20 23 26  29
# 3  3  6  9 12 15 18 21 24 27  30

 

 

다음으로, '변경 전 칼럼 이름 : 변경 후 칼럼 이름' 매핑 테이블을 만들어보겠습니다. 아래 예제에서는 변경 전 칼럼 이름 "X1"~"X10" 을 --> 변경 후 칼럼 이름 "var1"~"var10" 의 매핑 테이블 data.frame을 만들었습니다. (특정 칼럼만 선별적으로 변경하고 싶으면 해당 칼럼의 "변경 전 : 변경 후 매핑 테이블"을 만들면 됩니다.)

 

## -- creating a key(old column name):value(new column name) mapping table
old_col_nm <- names(df)
print(old_col_nm)
# [1] "X1"  "X2"  "X3"  "X4"  "X5"  "X6"  "X7"  "X8"  "X9"  "X10"

col_cnt <- ncol(df) # 10
new_col_nm <- paste0(c(rep("var", col_cnt)), 1:col_cnt)
print(new_col_nm)
# [1] "var1"  "var2"  "var3"  "var4"  "var5"  "var6"  "var7"  "var8"  "var9"  "var10"

df_col_dict <- data.frame("old_col_nm" = old_col_nm, "new_col_nm" = new_col_nm)
print(df_col_dict)
# old_col_nm new_col_nm
# 1          X1       var1
# 2          X2       var2
# 3          X3       var3
# 4          X4       var4
# 5          X5       var5
# 6          X6       var6
# 7          X7       var7
# 8          X8       var8
# 9          X9       var9
# 10        X10      var10

 

 

 

마지막으로, dplyr 패키지의 rename_at() 함수를 사용해서 "변경 전 칼럼 이름(old_col_nm)"을 "변경 후 칼럼 이름(new_col_nm)" 으로 변경해 보겠습니다. 

 

## -- changing data.frame's column names using key(old_col):value(new_col) mapping table
library(dplyr)
df_new <- df %>% 
  rename_at(vars(as.character(df_col_dict$old_col_nm)), 
            ~ as.character(df_col_dict$new_col_nm))

print(df_new)
# var1 var2 var3 var4 var5 var6 var7 var8 var9 var10
# 1    1    4    7   10   13   16   19   22   25    28
# 2    2    5    8   11   14   17   20   23   26    29
# 3    3    6    9   12   15   18   21   24   27    30

 

 

 

(2) 특정 칼럼만 선별적으로 이름을 바꾸고 싶은 경우

 

아래의 'col_dict' 테이블을 칼럼 이름을 변경하고자 하는 특정 칼럼의 old_col_nm : new_col_nm 으로 만들어서 적용하면 됩니다.

가령, 기존의 c1~c5'까지의 칼럼들 중에서 'c2', 'c4' 의 2개 칼럼만 선별적으로 변경하고 싶으면 아래처럼 'col_dict' 테이블을 만들어서 적용하면 돼요.

 

old_col_nm = c("c2", "c4")
new_col_nm = c("v2", "v4")

col_dict <- data.frame("old" = old_col_nm, "new" = new_col_nm)
print(col_dict)
# old new
# 2 c2 v2
# 4 c4 v4


library(dplyr)
c_df_new <- c_df %>%
rename_at(vars(as.character(col_dict$old)), ~ as.character(col_dict$new))

print(c_df_new)
# c1 v2 c3 v4 c5
# 1 1 4 7 10 13
# 2 2 5 8 11 14
# 3 3 6 9 12 15

 

이번 포스팅이 많은 도움이 되었기를 바랍니다. 

행복한 데이터 과학자 되세요!  :-)

 

728x90
반응형
Posted by Rfriend
,