no image
[Python] 기술통계
데이터: https://github.com/itggangpae/python_statistics 1. 통계 - 논리적 사고와 객관적인 사실에 따르며 일반적이고 확률적 결정론에 따라서 인과 관계를 규명 - 연구 목적에 의해 설정된 가설들에 대하여 분석 결과가 어떤 결과를 뒷받침하고 있는지를 통계적 방법으로 검정 - 분류 기술통계: 수집된 데이터의 특성을 쉽게 파악하기 위해서 데이터를 표나 그래프 또는 대푯값으로 정리 요약하는 통계 추론통계: 기술 통계량과 모집단에 추출한 정보를 이용해서 모집단의 특성을 과학적으로 추론 2. 변수(feature)의 종류 2-1) 질적변수와 양적변수 - 질적 변수: 구분을 위한 변수 범주형 명목척도, 순서척도 - 양적 변수: 양을 표현하는 변수 대부분 연속형 수치라고 표현해서는..
2024.02.22
no image
[Python] 이미지 데이터 다루기
1. Computer Vision 1-1) 영상 처리와 컴퓨터 비전 - 영상 처리 컴퓨터를 사용해서 입력 영상 데이터를 처리하는 분야 - 컴퓨터 비전 인간의 시각을 흉내내는 컴퓨터 프로그램 입력 영상에서 의미있는 정보를 추출하는 분야 제품의 결함을 검사하는 분야나 얼굴 인식이나 지문 인식이나 물체 검출 등이 컴퓨터 비전의 대표적인 분야 1-2) 이미지 처리 - 머신 러닝에서는 원본 이미지를 학습 알고리즘이 사용할 수 있도록 변환 수행 1-3) 컴퓨터 비전 분야 - Object Classification: 객체 분류, 이미지 분류 - Object Detection & Localization: 객체 식별 - Object Segmentation: 객체 분할 - Image Captioning: 이미지의 상황을 ..
2024.02.21
no image
[Python] 확률
1.주피터 노트북에서 수학 기호 사용 - TeX 라는 조판 언어를 이용해서 수식을 표현할 수 있음 - Markdown Cell을 생성한 후 셀 안에서 $ 기호를 이용해서 수기을 표현 - 그리스 문자는 \그리스문자이름 2. 공통 코드 import pandas as pd import numpy as np #dist 플롯이나 histogram import seaborn as sns #시각화에서 한글을 사용하기 위한 설정 import platform from matplotlib import font_manager, rc font_name = font_manager.FontProperties(fname="c:/Windows/Fonts/malgun.ttf").get_name() rc('font', family=fo..
2024.02.21
no image
[Python] 데이터 스케일링
1. Standardization (표준화) 1-1) 개요 - 다변량 분석에서는 각 컬럼의 숫자 데이터의 크기가 상대적으로 달라서 분석 결과가 왜곡되는 현상 발생 다변량 분석: 2개 이상의 컬럼을 같이 사용해서 수행하는 분석 - 상대적 크기 차이를 동일하게 맞추는 작업이 표준화 또는 스케일링 일반적으로 0 ~ 1이나 -1 ~ 1로 생성 방법을 달리하는 가장 큰 이유는 이상치 여부 때문 1-2) 표준값과 편차값 - 모든 값들의 표준 값 기준으로 차이를 구해서 비교 표준 값: (데이터 평균) /표준편차 표준 값 평균은 0, 표준 편차는 1 정규분포와 비교하기 쉽다. 편차 값: 표준값 * 10 + 50 양수로 바꿔주기. 음수는 오타나 오류를 많이 발생하고 직관적이지 않기 때문. 편차 값의 평균은 50, 표준 ..
2024.02.16
no image
[Python] 데이터 전처리
1. 중복 데이터 처리 - 하나의 데이터 셋에서 동일한 관측 값이 2개 이상 중복되는 경우 분석 결과를 왜곡할 수 있음 1) duplicated() - 데이터의 중복 여부를 bool의 Series로 리턴 2) drop_duplicates() - 매개변수를 대입하지 않으면 모든 컬럼의 값이 일치하는 경우 제거 - subset 옵션: [‘컬럼 이름’]이나 [[‘컬럼’, ‘이름’, ‘list’]]를 대입하면 중복을 제거하고 첫번째 데이터만 유지 - keep 옵션: 위의 경우에서 마지막 데이터를 유지 - inplace 옵션 : 원본에 직접 작업 pandas와 DataFrame은 원본을 복제해서 수정하고 리턴 실시간으로 대량 발생하는 데이터는 데이터베이스에 바로 저장 못함 데이터 -> csv 파일에 기록 (유효성..
2024.02.15
no image
[책] 일을 잘한다는 것 _ 야마구치 슈, 구스노키 겐
요약 기술이 고도로 성장하던 시기에는 과학과 기술, 노력이 중시됐다. 그러나 저성장의 성숙한 경제 시기로 접어든 요즘에는 기술만큼이나 예술과 감각, 개성과 다양성이 중요해졌다. 야마구치씨와 구스노키씨의 대화로 진행되는 형식을 따라 정리해보았다. 제로가 아니라 플러스를 원한다. 기술이 없다면 마이너스(-)지만, 일을 잘한다는 것은 플러스(+)를 만들어가는 것이다. 기술력이 상향평준화된 인력시장에서 이제 기술은 평균이다. 평균점에 돈을 지불하는 사람은 없을 것이다. 구스노키 "만두를 만들 줄 안다는 이유로 누군가 선택된다면 아마도 일손이 부족할 때일 겁니다. 그 분야의 인력이 부족한 상태에서는 기술이 효력을 발휘하죠. 만두를 만들 수 있는 사람 수가 수요에 비해 확연히 부족해진다면 만두를 만드는 기술은 엄청..
2024.02.14
no image
[Python] 데이터 시각화
1. 데이터 시각화 - matplotlib 1) 앤스콤 데이터 - 이 데이터는 4개의 데이터 그룹으로 구성 - 앤스콤 데이터 가져오기 #앤스콤 데이터 가져오기 import seaborn as sns anscombe = sns.load_dataset("anscombe") print(anscombe.head()) print(anscombe['dataset'].unique()) dataset x y 0 I 10.0 8.04 1 I 8.0 6.95 2 I 13.0 7.58 3 I 9.0 8.81 4 I 11.0 8.33 ['I' 'II' 'III' 'IV'] # 데이터셋 분리 #dataset 별로 분리 dataset_1 = anscombe[anscombe['dataset'] == 'I'] dataset_2 = ..
2024.02.14
no image
[Python] 데이터 탐색
1. 데이터프레임에서의 데이터 선택 1) 열 선택 - 데이터프레임['컬럼이름'] 또는 데이터프레임.컬럼이름 데이터프레임.컬럼이름 으로 접근할 때는 컬럼이름이 반드시 문자열이어야 함 - 하나의 컬럼이름을 이용해서 접근하면 Series로 리턴 2) 행 선택 - loc[인덱스이름] - iloc[정수형 위치 인덱스] - Series로 리턴 3) 셀 선택 - [컬럼이름][인덱스이름] - loc[인덱스이름, 컬럼이름] - iloc[행 위치 인덱스, 열 위치 인덱스] 4) 다중 선택 - list를 이용해서 선택 DataFrame이 리턴 #item.csv 파일을 읽어서 DataFrame 만들기 #csv를 읽을 때 확인할 3가지 #한글 포함 여부 - 인코딩 # 구분자는 , 인지 #첫번째 줄이 컬럼이름인지 아니면 데이터인..
2024.02.14

[Python] 기술통계

0ㅑ채
|2024. 2. 22. 12:22

데이터: https://github.com/itggangpae/python_statistics

 

1. 통계

- 논리적 사고와 객관적인 사실에 따르며 일반적이고 확률적 결정론에 따라서 인과 관계를 규명

- 연구 목적에 의해 설정된 가설들에 대하여 분석 결과가 어떤 결과를 뒷받침하고 있는지를 통계적 방법으로 검정

- 분류

  • 기술통계: 수집된 데이터의 특성을 쉽게 파악하기 위해서 데이터를 표나 그래프 또는 대푯값으로 정리 요약하는 통계
  • 추론통계: 기술 통계량과 모집단에 추출한 정보를 이용해서 모집단의 특성을 과학적으로 추론

 

 

2. 변수(feature)의 종류

2-1) 질적변수와 양적변수

- 질적 변수: 구분을 위한 변수

  • 범주형
  • 명목척도, 순서척도

- 양적 변수: 양을 표현하는 변수

  • 대부분 연속형
  • 수치라고 표현해서는 안됨. 범주형의 데이터를 머신러닝에 사용하기 위해서 원핫인코딩을 하면 수치 데이터로 변경이 되는데 양적 변수는 아니기 때문이다.  
    ex. 성별을 0과 1로 표현
  • 등간척도, 비율척도

 

2-2) 척도(scale) 수준

- 명목(nominal) 척도: 남자, 여자처럼 순서에 아무런 의미가 없는 척도

 

- 순서(ordinal) 척도: 상중하, 셋 사이에 얼마가 될지는 몰라도 분명히 크기의 차이가 있다.

 

- 등간(interval) 척도: 구분할 수 있는 범위가 있다. 

 

- 비율(ratio) 척도: 절대 영도가 있는지에 따라 등간척도와 구분한다.

  • 온도는 등간척도다. 끝이 없고 명확한 절대점이 없기 때문이다.

 

2-3) 데이터 가져오기

descriptive.csv
0.01MB

 

- 부모의 학력 수준에 따르나녀의 대학 진학 합격 여부를 조사한 데이터

- 300개 행, 8개 열

  • uresident: 거주지역(1,2,3 – 특별시, 광역시, 시군)
  • ugender: 성별(1,2 – 남, 여)
  • uage: 나이
  • ulevel: 학력 수준(1,2,3 – 고졸, 대졸, 대학원졸 이상)
  • ucost: 생활비
  • utype: 학교 유형(1,2)
  • usurvey: 만족도(1-5)
  • upass: 합격여부(1,2)
print('------데이터 읽기 ------')
print(university.head())
print('------데이터 크기 ------')
print(university.shape)
print('------데이터 요약 ------')
print(university.describe())
------데이터 읽기 ------
   resident  gender  age  level  cost  type  survey  pass
0       1.0       1   50    1.0   5.1   1.0     1.0   2.0
1       2.0       1   54    2.0   4.2   1.0     2.0   2.0
2       NaN     1   62    2.0   4.7   1.0     1.0   1.0
3       4.0       2   50  NaN   3.5   1.0     4.0   1.0
4       5.0       1   51    1.0   5.0   1.0     3.0   1.0
------데이터 크기 ------
(300, 8)
------데이터 요약 ------
            resident   gender      age          level     cost      type          survey     pass
count   279.000  300.000  300.000  287.000  271.000  274.000  187.000  279.000
mean      2.233    1.420   53.880    1.836    8.723       1.281         2.594    1.434
std       1.484    0.546      6.813        0.792   68.971    0.474         0.976    0.496
min       1.000    0.000   40.000      1.000 -457.200(?)  1.000       1.000    1.000
25%       1.000    1.000   48.000    1.000    4.400       1.000          2.000     1.000
50%       2.000    1.000   53.000    2.000    5.400       1.000           3.000     1.000
75%       3.000    2.000   60.000    2.000    6.300       2.000           3.000     2.000
max       5.000    5.000   69.000    3.000  675.000(?)  4.000         5.000    2.000

 

 

2-4) 명목 척도

- 구별만을 위해서 의미 없는 수치로 구성한 데이터

- 거주지역, 성별 등

- 요약 통계량 의미 없음

- 데이터 개수 정도만 확인 - 이상치 확인 가능

print(university['gender'].value_counts())
gender
1    173
2    124
0      2
5      1
Name: count, dtype: int64
  • 0과 5는 이상한 데이터. 지우자.
  • 방법은 두가지. drop을 사용해서 직접 제거하거나 올바른 데이터만 필터링 하거나.
#데이터 정제
university_gender = university[(university['gender'] == 1) | (university['gender'] ==2)]
print(university_gender['gender'].value_counts())
gender
1    173
2    124
Name: count, dtype: int64

 

#범주형 데이터 시각화
university_gender['gender'].value_counts().plot.bar(color='k', alpha=0.7)

 

 

2-5) 순서 척도

- 순서를 정하기 위해 만든 수치데이터

- 계급순위를 수치로 표현한 직급, 학력 수준 등

- 기초 통계량 중에서 빈도 수 정도만 의미

print(university_gender['level'].value_counts())
level
1.0    115
2.0     99
3.0     70
Name: count, dtype: int64

 

 

2-6) 등간 척도

- 속성의 간격이 일정한 값을 갖는 척도

- 만족도처럼 각 데이터끼리 비교가 가능하고 가중치 적용 등이 가능

- 기술통계량은 의미를 갖지만 산술 연산은 하지 않음

  • 평균을 구하기 위해서 합계를 구하기는 하지만 합계 자체는 의미가 없음

- 절대 원점없음

#등간 척도 데이터 시각화
university_gender['survey'].value_counts().plot.pie()
plt.show()

 

 

2-7) 비율 척도

- 등간 척도의 특성에 절대 원점이 존재

- 특정 값을 기준으로 한 수치 데이터라서 사칙 연산이 의미를 갖는 척도

  • 빈도를 직접 구하지는 않는 경우가 많다. 대부분 일정한 범위로 편집을 해서 빈도를 구한다.
  • 직접 입력받는 경우가 많아서 이상치 탐지를 수행해야 한다. 

- 점수, 나이, 무게, cost 등

 

#cost 값이 2~10인 데이터만 추출

cost = university_gender[(university_gender['cost']>=2) & (university_gender['cost']<=10)]
cost['cost'].describe()
count    248.000
mean       5.354
std        1.139
min        2.100
25%        4.600
50%        5.400
75%        6.200
max        7.900
Name: cost, dtype: float64

 

#범주화

cost = university_gender['cost']
cost = cost[(cost>=2) & (cost<=10)]

#범주화 2~3: 1    3~6: 2     6~: 3
cost[(cost>=2)&(cost<=3)] = 1
cost[(cost>3)&(cost<=6)] = 2
cost[(cost>6)] = 3

#정수로 변환
cost = cost.astype(int)

print(cost.value_counts())
cost
2    157
3     82
1      9
Name: count, dtype: int64

 

# 시각화

label = ["하", "중", "상"]

plt.pie(cost.value_counts(), labels=label, autopct='%1.1f%%')
plt.title('생활비 분포')
plt.legend()
plt.show()

 

 

2-8) 이산형 데이터와 연속형 데이터

- 이산형 데이터: 하나하나의 값을 취하고 서로 인접한 숫자 사이에 값이 존재하지 않는 변수

  • ex. 주사위의 눈

- 연속형 데이터: 연속적인 값을 취할 수 있는 데이터. 어떤 두 숫자 사이에 반드시 숫자가 존재하는 형태로 길이나 무게 등

  • 연속형 데이터라고 하더라도 측정 정밀도에 한계가 있어서 실제로는 띄엄띄엄 값을 취할 수밖에 없음
  • 키를 소수점 한자리까지의 정밀도로 측정하면 170.3과 170.4 사이에 다른숫자가 없는 것으로 간주
  • 이런 경우 이산형 데이터지만 연속형 데이터로 취급

 

 

 

3. 대표값

- 평균이나 분산 등의 수치 데이터를 요약해서 파악

- 그래프를 그려서 시각적으로 데이터 파악

 

- 대표값: 데이터를 하나의 값으로 요약한 지표

3-1) 평균 (mean)

- 모든 값의 총합을 데이터 개수로 나눈 값 - 산술 평균

 

- 기하 평균: 비율의 평균을 구할 때 사용. 각 데이터의 비율을 곱한 후 제곱근 구하는 것. 

  • 매출액이 100인 회사에서 다음 해의 매출이 110을 기록하고 그 다음해 매출이 107.8을 기록했을 때 연평균 성장률은?
  • 산술평균으로 구하면 첫 해에 10%, 다음 해에 2% 증가했으니 8 / 2 = 평균 4%가 된다.
  • 1.1 * 0.98의 제곱근으로 구해야 함!
from pandas import Series, DataFrame
import pandas as pd
import numpy as np
import math

s = Series([10, 11, 10.78])
print("평균 성장률이라고 생각하는 답:", s.pct_change().mean())
print("평균 성장률(기하평균):", math.sqrt((11/10)*(10.78/11)))
평균 성장률(산술평균): 0.040000000000000036 
평균 성장률(기하평균): 1.0382677881933928

 

- 조화 평균: 속도의 평균을 구할 때 사용.

  • 데이터 전부 곱함 x 2 / 데이터의 합
  • 동일한 거리를 한 번은 시속 100km로 달리고 한 번은 60km로 달렸을 때 평균 속도는?
  • 300km를 달렸다고 치면, 3시간 + 5시간 총 8시간이 나와야 한다.
print("평균 속도라고 생각하는 답" , (600 / 80))
print("평균 속도(조화평균):", (600 / ((2*100*60) / (100+60))))
평균 속도라고 생각하는 답: 7.5
평균 속도(조화평균): 8.0

 

- 가중평균(Weighted Mean)

  • 가중치를 곱한 값의 총합을 가중치의 총합으로 나눈 값
  • 데이터를 수집할 때 모든 사용자 그룹에 대해서 정확히 같은 비율의 데이터를 수집하기는 어렵기 때문에 가중치 부여해서 평균 구함
  • 여론 조사에서 많이 사용
  • 어느 정당을 지지하는지 알고 싶어서 샘플링을 수행해야 하는데 샘플링을 실제 투표권자의 비율대로 수집하는 것은 거의 불가능
  • 샘플링한 후 샘플링된 데이터에 투표권자의 비율을 가중치로 곱해서 가중치의 합으로 나누는 방식 채택
  • numpy.average 함수 이용해서 weights에 가중치 설정

- 이상치 (Outlier)

  • 대부분의 값과 매우 다른데이터 값(극단값)

- Robust

  • 극단값이나 이상치에 민감하지 않은 것
  • resistant, 저항성이 있다고도 함

- Trimmed Mean(절사 평균)

  • 정해진 개수의 극단 값을 제외한 나머지 값들의 평균
  • 이상치를 제거하고 구한 평균은 실제로는 절사 평균

- 이동평균 (moving average)

  • 데이터가 방향성을 가지고 움직일 때 (시계열 데이터가 대표적) 이동하면서 구해지는 평균
  • 단순이동평균(SMA - Simple Moving Average)
    • n번째 데이터를 포함한 왼쪽 m개(window) 데이터의 평균
    • 실제 함수는 매번 단순 이동 평균을 직접 계산하지 않고 (이전의 이동 평균) + 새로운 데이터 - 가장 오래된 데이터 를 계산하고 윈도우 크기로 나눠서 계산
  • 누적이동평균(CMA - Cumulative Moving Average)
    • 단순 이동 평균과 계산법은 같은데 윈도우 크기를 설정하지 않고 새로운 값이 들어올 때마다 전체 평균을 다시 계산
  • 선형가중이동평균(WMA - Weighted Moving Average)
    • 단순이동평균처럼 윈도우 개수(m)을 설정해서 구하는데 가중치가 선형으로 변경
    • dot product 연산 수행: ([m, m-1, m-2, ..., 1] * [n번째 데이터, n-1번째 데이터, ... , n-m+1번째 데이터])
      - 행렬의 곱 (열이 하나면 행도 하나, 길이가 같아야 한다.)
    • 오래된 데이터일수록 가중치가 선형적으로 감소한다!
    • 최신의 데이터에 큰 가중치를 제공해서 최신의 데이터가 결과에 더 큰 영향력을 발휘하게 함
    • 목적에 맞추어 조금 더 유연하게 비선형적인 방식으로 곱해나가기도 함. 

  • 지수가중이동평균(EWMA - Exponentially Weighted Moving Average)
    • span(알파값): 가중치 (0~1사이의 값)
    • 첫번째 지수가중이동평균: 1번째 데이터
    • n번째 지수가중이동평균: (1 - 알파) * (n-1번째 지수가중이동평균) + 알파 * 현재 데이터 값
    • 알파 값은 일반적으로 2/(windows 개수 +1)로 설정

 

3-2) 중앙값(median)

  • 데이터를 크기 순서대로 나열할 때 정확하게 중앙에 위치한 값
  • 중앙값은 평균에 비해서 이상치에 강하다는 특성이 있음
    • 데이터가 1, 2, 3, 4, 5, 6, 1000 이 있을 경우
      평균: 145.85
      중앙값: 4
    • 위의 데이터를 더 잘 설명한 값은 중앙값
  • 데이터가 짝수개라면 중앙값은 중앙에 위치한 2개 데이터의 평균
### 중앙값
tdata = pd.read_csv('./python_statistics-main/data/tdata.csv', encoding='cp949')
tdata.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 10 entries, 0 to 9
Data columns (total 2 columns):
 #   Column  Non-Null Count  Dtype
---  ------  --------------  -----
 0   번호      10 non-null     int64
 1   성적      10 non-null     int64
dtypes: int64(2)
memory usage: 292.0 bytes
from scipy import stats

print('평균:', tdata['성적'].mean())
print('중간값:', tdata['성적'].median())
#상위와 하위 10% 잘라내고 평균 구하기
print('절사평균:', stats.trim_mean(tdata['성적'], 0.1))
평균: 77.1
중간값: 77.5
절사평균: 77.0
  • 3개의 값이 별 차이가 없으니 크게 이상한 데이터가 없는 것
state = pd.read_csv('./python_statistics-main/data/state.csv')
state.head()


Population 데이터의 편차가 큼
print('단순 평균:', state['Population'].mean())
print('중앙값:',  state['Population'].median())
print('절사평균:', stats.trim_mean(state['Population'], 0.1))
단순 평균: 6162876.3
중앙값: 4436369.5
절사평균: 4783697.125
  • 단순 평균과 중앙값이 차이가 많이 난다. 
  • 중앙값은 실제 데이터다보니 절사평균 사용을 더 선호

- 가중 중앙값

  • state데이터에는 Murder.Rate가 살인 사건 발생 비율인데 살인 사건의 평균 비율을 알고자 할 때 단순하게 Murder.Rate의 평균을 구하는 것은 바람직하지 않다. 인구가 많은 곳에 가중치를 높게 설정하고, 인구가 작은 지역은 가중치를 적게 설정해서 평균을 구해야 한다.
!pip install wquantiles

import wquantiles
#가중 평균을 구하기 위해서는 numpy 의 average 함수를 이용
print(state['Murder.Rate'].mean())
print(np.average(state['Murder.Rate'], weights=state['Population']))

#가중 중앙값은 wquantiles 패키지의 median을 이용 - 설치 해야 함
print(wquantiles.median(state['Murder.Rate'], weights=state['Population']))
4.066  #평균
4.445833981123393  #가중평균
4.4  #가중 중앙값

 

 

3-3) 최빈값(mode)

- 데이터베이스 가장 많이 나타나는 값

- Series나 DataFrame에서 mode 함수를 호출하여 구해준다.

- 질적 데이터의 대표값을 구할 때 이용

print(state['Murder.Rate'].mode())
print(university['age'].mode())
0    1.6
1    2.0
2    5.7
Name: Murder.Rate, dtype: float64

0    48
Name: age, dtype: int64

 

 

 

3-4) 변이 추정

- 개요

  • 데이터 값이 얼마나 밀집해 있는지 혹은 퍼져있는지를 나타내는 것으로 분산(dispersion)이라고도 함
  • 변이를 측정해서 실제 변이와 랜덤을 구분하고 실제 변이의 다양한 요인들을 알아보고 변이가 있는 상황에서 결정을 내리는 등의 작업을 위해 추정

- 지표

  • 편차(Deviation)
    • 관츨된 데이터와 위치 추정을 위한 값(평균) 사이의 차이로 오차 또는 잔차(residual)
    • 편차는 음과 양의 부호로 존재해서 평균을 구하려 하면  서로 상쇄하여 0에 가까운 값이 되어버림
  • 분산(Variance)
    • 평균과의 편차의 제곱한 값들의 합을 n이나 n-1로 나눈 값인데 평균제곱오차라고도 함
    • 제곱을 해서 더한 값이기 때문에 원본 데이터와 스케일이 다를 가능성이 높아 바로 사용하기는 어렵다. 
  • 표준편차(Standard Deviation)
    • 분산의 제곱근, L2 norm 또는 유클리드 norm
  • 평균절대오차(Mean Absolute Deviation)
    • 평균에 대한 편차의 절대값의 평균으로 L1 norm 또는 맨허튼 norm
  • 중앙값의 중위절대편차(Median Absolute Deviation from the Median)
    • 중앙값과의 편차의 절대값의 중앙값
  •  
  • 범위(range)
    • 최대값과 최소값의 차이
    • 데이터를 scailing 할 때 0~1 사이로 만드는 방법은 (데이터-최소값) / (최대값-최소값)
  • 순서 통계량(Order Statistics)
    • 최소에서 최대까지 정렬된 데이터 값에 따른 계량형 순서
  • 백분위수(Percentile)
    • 분위 수, P 퍼센트 값.
  • 사분위 범위(Interquartile Range)
    • 75번째 백분위 수와 25번째 백분위수 사이의 차이로 IQR 이라고도 함
  • 자유도(degrees of freedom , df)
    • 과학 분야에서는 독립적으로 달라질 수 있는 시스템의 매개변수 개수
    • 평면에서의 한 점은 평행 이동을 할 때 X와 Y좌표 2개를 자유롭게 가질 수 있다. (자유도=2)
    • 통계학에서는 통계적 추정을 할 때 표본 자료 중 모집단에 대한 정보를 주는 독립적인 자료의 수
    • 표준편차나 분산은 표본의 평균에 따른다는 제약조건을 가지기 때문에 1개의 데이터는 독립적이지 못해서
      n-1로 자유도를 설정
    • ANOVA(분산분석)처럼 다수의 집단을 고려할 때는 자유도가 n - 집단의개수

- 분산, 표준편차, 평균절대편차 등은 특이값과 극단값에 로버스트 하지 않기 때문에 중앙값(중위절대편차) 이용

  •  MAD = median( |데이터 - 그룹의중앙값| )
  • MAD를 구해주는 함수는 statsmodel.robust.scale.mad 함수
state = pd.read_csv('./python_statistics-main/data/state.csv')

#표준 편차
print(state['Population'].std())

#IQR - 사분위 범위
print(state['Population'].quantile(0.75) - state['Population'].quantile(0.25))

#중위 절대 편차(MAD)
from statsmodels import robust
print(robust.scale.mad(state['Population']))
print(abs(state['Population'] - state['Population'].median()).median() / 0.6744897501960817)
6848235.347401142

4847308.0

3849876.1459979336
  • IQR은 이상치 검출할 때도 이용
    IQR * 1.5를 한 값에 구한 후 25% 숫자에서 뺀 값보다 작은 값이나 75% 숫자에 더한 값보다 큰 값을 이상치로 판정
    but 데이터 개수가 12개보다 적으면 검출 불가
    이런 경우 중위값을 가지고 보정해서 구하기도 함

 

 

 

4. 데이터의 분포 탐색

분포 탐색을 위한 시각화

- Box Plot(상자 그림)

  • 사각형과 수염을 이용해서 중앙값과 4분위수 그리고 극단치 등을 표시해주는 그림
  • 극단치가 있는 경우 (1) 데이터를 스케일링(단위 조정)해서 극단치의 영향력을 감소시키거나 (2) 극단치를 제거하고 사용하기도 하고 (3) 극단치를 이용해서 별도의 변수를 생성해서 데이터분석에 활용하기도 함
    • 비의 양을 데이터화 시킬 때 우리나라의 경우 하루에 보통 0 ~ 100mm 정도 온다고 가정했을 때 비가 1000mm 온 날이 있다면 비가 500mm이상 온 변수를 생성해서 True와 False로 설정
  • 상자 그림은 중앙값과 4분위수, 극단치를 파악하는 데는 좋지만 실제 데이터의 분포를 알아보는 것은 불가
  • 데이터의 분포를 확인하고자 하는 경우 scatter(산포도)나 바이올린 차트, swart 차트 이용

- 도수 분포표(Frequency Table)

  • 어떤 구간에 해당하는 수치 데이터 값들의 빈도를 나타내는 기록

- 히스토그램

  • 도수 분포표의 내용을 그래프로 표현

- 밀도 그림(Density Plot)

  • 히스토그램을 부드러운 곡선으로 나타낸 그림으로 KDE(Kernel Density Estimation - 커널 밀도 추정)를 주로 이용
  • 밀도 그림을 그려서 어떤 확률 분포를 따르는지 파악

# 상자 그림

ax = (state['Population']/1_000_000).plot.box(figsize=(3, 4)) #천단위 구분기호
#현재는 ax = (state['Population']).plot.box(figsize=(3, 4))라고 자동으로 추정

ax.set_ylabel('Population (millions)')
plt.tight_layout()
plt.show()

# 도수 분포표

  • 인구 수를 10개의 구간으로 분할
binnedPopulation = pd.cut(state['Population'], bins=10)
binnedPopulation.value_counts()
Population
(526935.67, 4232659.0]      24
(4232659.0, 7901692.0]      14
(7901692.0, 11570725.0]      6
(11570725.0, 15239758.0]     2
(15239758.0, 18908791.0]     1
(18908791.0, 22577824.0]     1
(22577824.0, 26246857.0]     1
(33584923.0, 37253956.0]     1
(26246857.0, 29915890.0]     0
(29915890.0, 33584923.0]     0
Name: count, dtype: int64

 

#히스토그램

ax = (state['Population']).plot.hist(figsize=(4, 4))
  • 밀도 추정 - 데이터의 분포를 부드러운 곡선으로 표현
ax = (state['Population']).plot.hist(density=True, xlim=[0, 12], 
                                    bins=range(1, 12), figsize=(4, 4))
state['Murder.Rate'].plot.density(ax=ax)

 

 

 

 

5. 다변량 탐색

5-1) 개요

-  일변량 분석 :평균이나 분산같은 추정값들은 한번에 하나의 변수를 다루는 것

- 이변량 분석(bivariate analysis) : 두개의 변수 간 관계를 파악하는 것

- 다변량 분석(multivariate analysis) : 두개 이상의 변수의 관계를 파악하는 것

 

- 다변량 분석에서 많이 사용하는 시각화

  • 분할표(Contingency Table): 두가지 이상의 범주형 빈도수를 기록한 표
  • 육각형 구간: 두 변수를 육각형 모양의 구간으로 나눈 그림
  • 등고 도표
  • 바이올린 도표

 

5-2) 교차 분석

- 범주형 자료를 대상으로 2개 이상의 변수들에 대한 관련성을 알아보기 위해서 결합 분포를 나타내는 교차분할표 작성

   => 상호 간의 관련성 여부 분석

- 교차 분석에서 사용되는 변수는 값을 10가지 미만으로 갖는 것이 좋다. 

- 교차 분석을 할 때는 범주형 데이터가 수치로 만들어져 있는 경우 다시 원래 의미로 변환해서 사용하는 것이 좋다.

  • 원본을 바꾸기보다 컬럼을 추가하는게 더 좋음. 수치로 변환했으면 이유가 있을테니까!
university = pd.read_csv('./python_statistics-main/data/descriptive.csv')

#gender 1:남자, 2: 여자
#성별 필드를 추가해서 남자와 여자로 기록
university['성별'] = '남자'

idx = 0
for val in university['gender']:
    if val == 2:
        university['성별'][idx] = '여자'
    idx = idx + 1
print(university['성별'].value_counts())
성별
남자    176
여자    124
Name: count, dtype: int64
#level 1: 고졸, 2:대졸, 3:대학원졸 이상, 나머지는 응답 없음
university['학력'] = '응답없음'

idx = 0
for val in university['level']:
    if val == 1.0:
        university['학력'][idx] = '고졸'
    elif val == 2.0:
        university['학력'][idx] = '대졸'
    else:
        university['학력'][idx] = '대학원졸'
        
    idx = idx + 1
#교차 분할표 생성은 pandas의 crosstab 함수 활용
print(pd.crosstab(university['학력'], university['성별']))
성별    남자  여자
학력          
고졸    67  50
대졸    60  40
대학원졸  49  34

 

 

5-3) 공분산(covariance)

- 2 종류의 데이터를 가지고 결합 분포의 평균을 중심으로 각 자료들이 어떻게 분포되어 있는지를 보여주는 수치

- 공분산이 분산과 다른점은 가로축과 세로축의 데이터가 다르기 때문에 편차들로 만든 도형이 직사각형이 되고 음의 면적도 만들 수 있음

  • (x1 - xM)(y1-yM) + (x2 - xM)(y2-yM) ... + (xn - xM)(yn-yM) / 데이터개수(편향)로 나눈 값 
    • 일반적으로 데이터개수보다   N - 1 - 절편향 
  • 키와 체중의 공분산을 구하는 경우
    • (첫번째 키 - 키의 평균)(첫번째 체중 - 체중의 평균)
      + (두번째 키 - 키의 평균)(두번째 체중 - 체중의 평균) 
      + (n번째 키 - 키의 평균)(n번째 체중 - 체중의 평균)
    • 이렇게 구해진 값을 데이터 개수로 나누면 됨
  • 공분산 > 0 : 변수 한쪽이 큰 값을 갖게 되는 경우 다른 한쪽도 커진다.
  • 공분산 < 0 : 변수 한쪽이 큰 값을 갖게 되는 경우 다른 한쪽은 작아진다.
  • 공분산 = 0 : 두개의 변수 사이에는 연관성이 없다.

- numpy.cov 함수: 공분산 값을 구함

  • 분산- 공분산 행렬 리턴

- ddof 옵션: 제약조건 개수 설정

- 자유도: 데이터의 수 - 제약조건의 수 

#공분산 직접 계산
cov_data = pd.read_csv('./python_statistics-main/data/cov.csv')
#공분산을 구하기 위해서는 평균을 알아야 하고 데이터 개수도 알아야 함
N = len(cov_data)
print(N) # 10
#x와 y의 평균
x = cov_data['x']
y = cov_data['y']

mu_x = np.mean(x)
mu_y = np.mean(y)

print(mu_x, mu_y)
21.020000000000003 
42.7
#자유도를 N-1로 해서 공분산 구하기
cov = sum((x - mu_x) * (y - mu_y)) / (N-1)
print("공분산:", cov)
공분산: 7.673333333333336
#자유도를 N으로 설정해서 분산-공분산 행렬 구하기
np.cov(x, y, ddof = 0)
array([[ 3.282,  6.906],
       [ 6.906, 25.21 ]])
#자유도를 N-1으로 설정해서 분산-공분산 행렬 구하기
np.cov(x, y, ddof = 1)
array([[ 3.646,  7.673],
       [ 7.673, 28.011]])

 

 

 

5-4) 상관계수

- 공분산은 단위나 자료의 범위에 따라 값의 차이가 크게 발생하기 때문에 여러 컬럼들 사이의 관련성을 확인하긴 어렵다.

- 2개 변수의 관계를 파악할 때 방향성만 분리해서 보는 것이 유용하기 때문에 새로운 지표를 생성

  • 공분산으로 연산을 수행해서 -1 ~ 1 사이의 값으로 변경한 것이 상관계수

- 수식

  • 상관계수 = 공분산 / (각 열의 표준 편차를 곱한 값)

- 해석

  • 부호 자체는 공분산과 동일하게 분석
  • 절대값 0.9 이상이면 매우 높은 상관관계
  • 절대값 0.7 이상이면 높은 상관관계
  • 절대값 0.4 이상이면 상관관계가 다소 높다.
  • 그 이외에는 상관관계가 약하거나 없다고 판정

 

상관계수나 공분산만으로 데이터를 파악할 수 없고 실제 분포도 확인해야 함!

상관 계수를 구하기 전에 산점도 등을 통해서 상관 계수를 구하는 것이 의미가 있는지 확인

  • matplotlib.pyplot 의 scatter 함수 이용
  • pandas 의 plot 함수를 호출, kind = scatter를 설정
  • seaborn 의 fairplot 함수를 이용할 수 있는데 이 함수는 DataFrame을 이용하면 모든 숫자 컬럼의 산점도를 모두 출력
  • eaborn에서는 regplot(산점도 와 회귀식) 이나 jointplot(산점도 와 히스토그램) 같은 두 개의 컬럼 만으로 산점도 와 히스토그램을 같이 그릴 수 도 있습니다.

- 상관계수의 종류

  • 피어슨 상관계수
  • 스피어만 상관계수
  • 켄달 상관계수

 

- 피어슨 상관계수

  • 일반적인 상관계수
  • 특잇값에 영향을 많이 받음
  • 선형 관계만 파악 가능
  •  pandas :  Dataframe에 corr() 함수 이용
  • scipy : scipy.stats 패키지에서도 pearsonr 함수 이용 : 피어슨 상관계수와 유의확률(p-value) 리턴

 

# 상관계수: 공분산 / (x의 표준편차 * y의 표준편차)

# -1 ~ 1사이의 숫자로 표준화

#auto-mpg 데이터 읽어오기
mpg = pd.read_csv('./data/auto-mpg.csv', header=None)
mpg.columns = ['mpg','cylinders','displacement','horsepower','weight',
              'acceleration','model year','origin','name']
#데이터프레임에 존재하는 모든 숫자 컬럼들의 산점도 전부 출력
import seaborn as sns
sns.pairplot(mpg)

 

mpg[['mpg', 'cylinders', 'displacement', 'weight']].corr()

 

#산점도 그래프

mpg.plot(kind='scatter', x='weight', y='mpg', c='coral', s=10)

 

#회귀선 출력

sns.regplot(x='weight', y='mpg', data=mpg)
sns.jointplot(x='weight', y='mpg', kind='reg', data=mpg)

 

csv나 txt 파일 형식으로 많은 양의 데이터를 저장해야 할 때 가장 좋은 방법은 적절한 크기로 분할해서 저장하는 것. 그런데 일반적으로 로그 파일을 만들 때는 1일 단위로 작성하는 경우가 많아서 어쩔 수 없이 파일의 크기가 커지는 경우가 있다. 이런 경우 gz로 압축해서 제공하면 pandas가 속도는 느려지지만 읽을 수 있다.
machine learning 패키지들은 디렉토리 단위로 데이터를 읽는 것이 가능

sp500_sym = pd.read_csv('./python_statistics-main/data/sp500_sectors.csv')
sp500_px = pd.read_csv('./python_statistics-main/data/sp500_data.csv.gz', index_col=0)

 

- 데이터의 상관관계를 상관계수로만 판단하면 안됨

  • 상관계수로 분포의 형상을 추측할 때 개별 자료가 상관계수에 미치는 영향력이 서로 다름
  • 피어슨 상관계수는 이상치나 극단치에 영향을 크게 받는 경우가 발생
  • 앤스콤 데이터
    • 4그룹의 데이터를 가지고 있는데 각 데이터 그룹의 상관계수가 0.816으로 동일
    • 첫번째 그룹은 일반적인 데이터 그룹, 두번째 그룹은 완전한 비선형 상관관계를 가진 데이터셋인데 피어슨 상관계수가 비선형 관계는 정확히 반영하지 못하기 때문에 0.816의 값을 가지고
    • 세번째 그룹과 네번째 그룹은 이상한 데이터가 1개가 영향력을 크게 미쳐서 상관계수가 0.816dl이 됨

 

# 앤스콤 데이터

import statsmodels.api as sm
data = sm.datasets.get_rdataset('anscombe')
df = data.data
#피어슨 상관계수 확인
print(df['x1'].corr(df['y1']))
print(df['x2'].corr(df['y2']))
print(df['x3'].corr(df['y3']))
print(df['x4'].corr(df['y4']))
0.81642051634484
0.8162365060002428
0.8162867394895984
0.8165214368885028
plt.subplot(221)
sns.regplot(x='x1', y='y1', data=df)
plt.subplot(222)
sns.regplot(x='x2', y='y2', data=df)
plt.subplot(223)
sns.regplot(x='x3', y='y3', data=df)
plt.subplot(224)
sns.regplot(x='x4', y='y4', data=df)
  • 첫번째는 일반적인 데이터. 납득 가능
  • 두번째는 비선형이다. 그런데 상관계수는 같다. 
  • 세번째, 네번째는 상관관계가 없는데 이상치 하나 때문에 영향을 받는다.

 

- 스피어만 상관계수

- 순위 기반으로 상관계수를 만들어내는 방식

- 값을 가지고 하지 않기 때문에 비선형 관계도 파악 가능

- 순위를 기반으로 하기 때문에 연속형 데이터가 아니어도 적용 가능

-  pandas :   corr()의 method 옵션 = spearman 설정

-  scipy : scipy.stats.spearmanr(데이터 1개, 데이터 1개, axis=0)

 

- 스피어만 상관계수 확인

s1 = pd.Series([1, 2, 3, 4, 5])
s2 = pd.Series([1, 4, 9, 16, 25])

sns.regplot(x=s1, y=s2)

#피어슨 상관 계수
print("피어슨 상관 계수:", s1.corr(s2))

#스피어만 상관 계수
print("스피어만 상관 계수:", s1.corr(s2, method='spearman'))
import scipy as sp

# 앤스콤 데이터의 스피어만 상관 계수
print("1의 스피어만 상관 계수: ", sp.stats.spearmanr(df['x1'], df['y1']))
print("2의 스피어만 상관 계수: ", sp.stats.spearmanr(df['x2'], df['y2']))
print("3의 스피어만 상관 계수: ", sp.stats.spearmanr(df['x3'], df['y3']))
print("4의 스피어만 상관 계수: ", sp.stats.spearmanr(df['x4'], df['y4']))
1의 스피어만 상관 계수:  SignificanceResult(statistic=0.8181818181818182, pvalue=0.0020831448404786904)
2의 스피어만 상관 계수:  SignificanceResult(statistic=0.690909090909091, pvalue=0.018565033381595004)
3의 스피어만 상관 계수:  SignificanceResult(statistic=0.990909090909091, pvalue=3.762571807085399e-09)
4의 스피어만 상관 계수:  SignificanceResult(statistic=0.5, pvalue=0.11730680301423815)

 

 

- 켄달의 상관계수

- 스피어만 상관계수와 비슷한데 순위를 기반으로 하는게 아니라 값의 증감만 확인

- pandas : method=kendall

- scipy : scipy.stats.kendalltau 호출

print("켄달 상관 계수:", s1.corr(s2, method='kendall'))

print("1의 켄달 상관 계수: ", sp.stats.kendalltau(df['x1'], df['y1']))
print("2의 켄달 상관 계수: ", sp.stats.kendalltau(df['x2'], df['y2']))
print("3의 켄달 상관 계수: ", sp.stats.kendalltau(df['x3'], df['y3']))
print("4의 켄달 상관 계수: ", sp.stats.kendalltau(df['x4'], df['y4']))
켄달 상관 계수: 0.9999999999999999

1의 켄달 상관 계수:  SignificanceResult(statistic=0.6363636363636364, pvalue=0.005707170915504249)
2의 켄달 상관 계수:  SignificanceResult(statistic=0.5636363636363636, pvalue=0.016540504248837583)
3의 켄달 상관 계수:  SignificanceResult(statistic=0.9636363636363636, pvalue=5.511463844797178e-07)
4의 켄달 상관 계수:  SignificanceResult(statistic=0.42640143271122083, pvalue=0.11384629800665805)
  • 스피어만 상관계수와 거의 유사하다. 

 

 

6. 분포 시각화

6-1) 수치형 데이터와 수치형 데이터의 분포 시각화

- 산점도

  • 데이터의 개수가 상대적으로 적을 때는 무난하지만 
  • 수십, 수백만의 레코드를 나타내는 경우에는 점들이 너무 밀집되어 있어서 알아보기 어려움
tips = sns.load_dataset('tips')
sns.jointplot(x='total_bill', y='tip', data=tips, kind='scatter')

 

- 육각형 구간 차트

  • 데이터를 점으로 표현하는 대신 기록값들을 육각형 모양의 구간들로 나누고 각 구간에 포함된 기록 값의 개수에 따라 색상을 표시
sns.jointplot(x='total_bill', y='tip', data=tips, kind='hex')

 

- 등고선 차트

  • 두 변수로 이루어진 지형에서의 등고선
  • matplotlib.pyplot : contoutf 함수 이용
  • seaborn : kdeplot 이용
  • 데이터가 많을 때 사용
kc_tax = pd.read_csv("./python_statistics-main/data/kc_tax.csv.gz")
print(kc_tax.shape)
(498249, 3)
#필터링
kc_tax0 = kc_tax.loc[(kc_tax.TaxAssessedValue < 750000) & 
                     (kc_tax.SqFtTotLiving > 100) &
                     (kc_tax.SqFtTotLiving < 3500), :]
print(kc_tax0.shape)
(432693, 3)
sns.kdeplot(data=kc_tax0, x="SqFtTotLiving", y="TaxAssessedValue")
plt.show()

 

 

6-2) 범주형 데이터와 범주형 데이터의 시각화

- 분할표 이용

  • 각 범주나 범주의 list를 인덱스와 컬럼에 배치해서 사용
  • crosstab이나 pivot_table 함수를 이용
  • pivot_table을 많이 이용하는데 이유는 피봇 테이블은 aggfunc라는 매개변수에 사용하고자 하는 함수를 적용할 수 있기 때문
    • pivot_table의 매개변수는 index에 컬럼이나 컬럼 list를 설정하고 columns에 동일하게 컬럼이나 컬럼의 list를 설정하고 aggfunc에 집계에 사용할 함수 설정
    • 컬럼들이 전부 범주형(명목척도와 순서척도)라면 데이터 개수를 세는 것 말고는 의미가 없음
  • lc_loans.csv 파일을 읽어서 grade와 status의 교차분할표 작성
lc_loans = pd.read_csv('./python_statistics-main/data/lc_loans.csv')
lc_loans.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 450961 entries, 0 to 450960
Data columns (total 2 columns):
 #   Column  Non-Null Count   Dtype 
---  ------  --------------   ----- 
 0   status  450961 non-null  object
 1   grade   450961 non-null  object
dtypes: object(2)
memory usage: 6.9+ MB
result = lc_loans.pivot_table(index='grade', columns='status', aggfunc=lambda x:len(x), margins=True)

 

# 비율 출력

# 비율 출력 - grade의 합계를 제외한 부분 가져오기
df = result.copy().loc['A':'G', :]
df.loc[:, "Charged Off":"Late"] = df.loc[:, "Charged Off":"Late"].div(df['All'], axis=0)
df['All'] = df['All'] / sum(df['All'])
df

 

 

 

6-3) 범주형과 수치형 데이터 시각화

- BoxPlot

  • pandas : boxplot 함수 제공

- ViolinPlot

  • BoxPlot은 데이터 범위 출력에 효율적이지만 밀도 추정 결과는 출력 불가
  • BoxPlot과 유사하지만 밀도 추정 결과를 두께로 출력
  • 사각형이나 수염의 개념은 없어서 이상치 탐색에는 적합하지 않음
  • 유사한 형태로 SwarmPlot : 색상을 칠하지 않고 점을 겹치지 않게 출력
  • seaborn : violinplot 함수 이용
airline_stats = pd.read_csv('./python_statistics-main/data/airline_stats.csv')
airline_stats.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 33468 entries, 0 to 33467
Data columns (total 4 columns):
 #   Column             Non-Null Count  Dtype  
---  ------             --------------  -----  
 0   pct_carrier_delay  33440 non-null  float64
 1   pct_atc_delay      33440 non-null  float64
 2   pct_weather_delay  33440 non-null  float64
 3   airline            33468 non-null  object 
dtypes: float64(3), object(1)
memory usage: 1.0+ MB

 

#박스플롯

airline_stats.boxplot(by='airline', column='pct_carrier_delay')

 

#바이올린 차트

sns.violinplot(data=airline_stats, x='airline', y='pct_carrier_delay')

 

 

 

 

 

 

 

'Python' 카테고리의 다른 글

[Python] 샘플링 _ 표본 추출  (1) 2024.02.26
[Python] 확률 분포 모형  (1) 2024.02.26
[Python] 이미지 데이터 다루기  (0) 2024.02.21
[Python] 확률  (0) 2024.02.21
[Python] 데이터 스케일링  (1) 2024.02.16

1. Computer Vision

1-1) 영상 처리와 컴퓨터 비전

- 영상 처리

  • 컴퓨터를 사용해서 입력 영상 데이터를 처리하는 분야

- 컴퓨터 비전

  • 인간의 시각을 흉내내는 컴퓨터 프로그램
  • 입력 영상에서 의미있는 정보를 추출하는 분야
  • 제품의 결함을 검사하는 분야나 얼굴 인식이나 지문 인식이나 물체 검출 등이 컴퓨터 비전의 대표적인 분야

 

1-2) 이미지 처리

- 머신 러닝에서는 원본 이미지를 학습 알고리즘이 사용할 수 있도록 변환 수행

 

1-3) 컴퓨터 비전 분야

- Object Classification: 객체 분류, 이미지 분류

- Object Detection & Localization: 객체 식별

- Object Segmentation: 객체 분할

- Image Captioning: 이미지의 상황을 텍스트로 설명하는 기술

- Object Tracking

- Action Classification

 

1-4) 컴퓨터 비전의 어려움

- 사람은 이미지를 보지만 컴퓨터는 숫자를 봄

- 사람은 적은 데이터로 이미지 유추가 가능하지만 컴퓨터는 많은 양의 데이터를 필요로 함

- AI 서비스 환경에서는 사용자 데이터가 실시간으로 쌓이고 이를 반영하기 위한 반복적인 학습이 필수인데, MLOps 나 DevOps를 처음부터 고려해서 설계된 체계적이고 견고한 시스템이 많지 않음

 

1-5) 비전 체험 서비스

- 구글 렌즈

- Which face is real: https://www.whichfaceisreal.com - 생성형 AI를 이용해서 가짜 얼굴 영상을 만들고 골라내는 서비스

 

1-6) 이미지 데이터 셋

- CIFAR-10: 10개의 클래스에 대한 6만개의 이미지 데이터셋

- fashion MNIST: 10개의 클래스에 대한 7만개의 흑백 이미지 데이터셋

- Image Net: 1400 만 개 정도의 공개 이미지 데이터 셋

  • 일상 생활에 사용하는 모든 이미지를 포함하고 있는 데이터 셋

- MS COCO: 이미지를 설명하는 캡션도 같이 제공

- Cityscapes: 자율 주행 관련 이미지

 -Open image: 구글이 제공하는 이미지로 라벨링 과 주석이 같이 있는 이미지 셋

  • 최근에는 음성까지 추가

 

 

2. Open CV

- intel에서 만든 영상 처리와 컴퓨터 비전 관련 오픈 소스 라이브러리

- C/C++ 로 구현했는데 여러 플랫폼에서 C, C++, Python, Java, C#, Javascript 언어로 사용 가능

- MMX나 SSE 명령어를 통해서 고속의 알고리즘을 구현해서 실시간 비전 응용에 강점을 가지고 있음

- 영상 및 비디오 입출력, 영상 처리, 컴퓨터 비전 관련 기본 알고리즘, 기계 학습 모듈이 내장되어 있음

- 최근의 버전에서는 딥러닝 모델도 추가

- CUDA(Compute Unified Device Architecture) 와 Open CL 인터페이스가 개발되어 사용하고 있음

- 공식 사이트

 

설치

- opencv-python 패키지 설치

pip install opencv-python
import cv2
cv2.__version__

 

 

 

3. 윈도우 제어

3-1) 윈도우 생성

- cv2.namedWindow(이름[, 플래그])

  • 플래그: 윈도우 크기 관련 옵션: cv2.WINDOW_NORMAL 이나 cv2.WINDOW_AUTOSIZE
  • 일반적으로 파이썬은 옵션의 값을 문자열로 설정하는데 Open CV에서는 상수를 이용해서 설정

 

3-2) 윈도우 출력

- cv2.imshow(윈도우이름, 윈도우에 표시되는 영상 배열 - numpy.ndarray)

 

3-3) 윈도우 파괴

- cv2.destroyAllWIndows()

 

3-4) 윈도우 생성 및 출력

#윈도우 생성
cv2.namedWindow("window")

#윈도우에 출력할 이미지
image = np.ones((200, 300), np.float64)

#윈도우에 이미지를 출력
cv2.imshow("window", image)

#무한 반복
while True:
    #키보드 입력을 받아서 27(esc)이면 무한 반복 종료
    key = cv2.waitKeyEx(100)
    if key == 27:
        break
        
cv2.destroyAllWindows()

 

 

 

4. 영상 입출력

4-1) 영상 가져오기

- cv2.imread("이미지 파일 경로", 이미지 옵션) -> numpy.ndarray

- 이미지 옵션

  • cv2.IMREAD_UNCHANGED: 알파채널 포함
  • cv2.IMREAD_GRAYSCALE: 흑백
  • cv2.IMREAD_COLOR: 컬러 영상의 기본
  • cv2.IMREAD_ANYDEPTH: 8비트 영상으로 생성
  • cv2.IMREAD_ANYCOLOR

 

4-2) matplotlib.pyplot.imshow

- matplotlib을 이용해서 이미지를 출력

- numpy 의 ndarray 나 PIL 이미지를 출력

- 컬러 이미지를 출력할 때 BGR 순으로 출력하기 때문에 빨강 색과 파랑 색이 반전되서 출력되므로 출력할 때 cv2.cvtColor(이미지 데이터, cv2.COLOR_BGR2RGB)를 이용해서 색상 값의 순서를 변경해 주어야 합니다.

 

 

4-3) 이미지를 흑백으로 출력하고 이미지 데이터의 차원을 확인

import cv2
from matplotlib import pyplot as plt

#이미지를 흑백으로 가져오기
image = cv2.imread("./data/redpanda.png", cv2.IMREAD_GRAYSCALE)

#이미지 출력
plt.imshow(image, cmap='gray')
plt.show()

*이미지 출처: https://mshk43.tistory.com/14
#흑백 이미지의 shape(차원): 2차원 좌표에 값이 있는 구조
#이미지와 관련된 딥러닝은 4차원 데이터만 다룬다.
print(image.shape)

#하나의 픽셀 값을 알고자 하는 경우
print(image[100, 100])

 

(732, 730)
66

 

 

4-4) 이미지를 컬러로 출력

#이미지를 컬러로 가져오기
#이미지를 가져올 때는 BGR 형태로 가져옵니다.
image = cv2.imread("./data/redpanda.png", cv2.IMREAD_COLOR)

# 컬러 이미지를 화면에 출력할 때는 R 과 B의 값을 스위치해야 합니다.
image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

#이미지 출력
#BGR의 형태로 출력: 빨강색과 파랑색의 값이 스위치 되어서 출력
#plt.imshow(image)
plt.imshow(image_rgb)
plt.show()
print(image_rgb.shape)#컬럼 이미지는 3차원
(732, 730, 3)

 

 

 

 

5. 배열 처리

- 이미지를 전처리하는 것은 numpy의 ndarray를 조작하는 것

- 차원을 변경하거나 타입을 변경하고자 하면 reshape, flattern, astype 같은 함수 이용

 

5-1) 이미지 크기 변경

- 머신러닝(+딥러닝)에서는 배열 데이터만 다룰 수 있음

*배열 데이터는 동일한 자료형이어야 하고 동일한 차원의 데이터여야 한다.

- 많은 양의 이미지 데이터를 가지고 학습을 하면 메모리 사용량이 많기 때문에 머신이 제대로 학습을 못할 수 있기 때문에 줄여서 학습을 수행하기도 함

- 머신 러닝에서 많이 사용하는 이미지 크기는 32 X 32, 64 X 64, 96 X 96, 256 X 256 등 

- resize(이미지데이터, (가로 크기, 세로 크기)): 이미지 데이터를 가로 크기 와 세로 크기에 맞게 사이즈 조정을 수행

 

# 이미지 차원 변경

#이미지를 흑백으로 가져오기
image = cv2.imread("./data/redpanda.png", cv2.IMREAD_GRAYSCALE)
print("원본 이미지 구조:", image.shape)

#1차원으로 구조를 수정
img = image.reshape(image.shape[0] * image.shape[1])
print(img.shape)

img = image.flatten()
print(img.shape)
원본 이미지 구조: (732, 730)
(534360,)
(534360,)

 

image = cv2.imread("./data/redpanda_64.png", cv2.IMREAD_GRAYSCALE)

#64*64로 이미지 크기를 변경
result = cv2.resize(image, dsize=(64, 64))

plt.imshow(result, cmap='gray')
plt.show()

 

 

5-2) 배열에서 특정 영역 선택

- ROI(Region of Interest - 관심 영역) 선택

  • 이미지를 가지고 예측을 할 때 이미지의 모든 데이터가 필요한 것이 아니기 때문에 예측을 하기 위해서 필요한 영역을 선택하는 것은 중요한 작업
  • 회귀나 분류에서 feature selection(피처의 중요도를 파악해서 특정 피처를 선택하는 것) 과 유사한 작업

- 특정 영역을 선택한 후 이 데이터만 추출해서 작업을 하기도 하고 마스킹을 하기도 함

img = cv2.imread('./data/redpanda.png', cv2.IMREAD_GRAYSCALE)

#특정 영역을 선택해서 마스킹: 0이 검정색 255가 흰색
img[300:400, 200:450] = 255

plt.imshow(img, cmap="gray")
plt.show()

 

 

5-3) 회전

- 이미지 머신 러닝(딥 러닝)의 결과가 정확하지 않은 경우 혹은 이미지 데이터가 부족한 경우 이미지 증강을 위해서 수행

- 고정된 카메라가 영상을 만든다면 모든 영상의 각도가 일정하겠지만, 실제 카메라를 들고 촬영한 영상은 동일한 이미지더라도 각도가 다른 경우가 있기 때문에 학습을 할 때 이 부분을 고려하는 것이 좋다.

 

- 2차원 배열을 수직, 수평 양축으로 뒤집기

  • cv2.flip(src, flipCode[,dst])
    • src: 뒤집을 이미지
    • dst: 결과를 저장할 배열 (리턴하기 때문에 생략 가능)
    • flipCode: 배열을 뒤집는 축. 0이면 X축 기준으로 뒤집고, 1이면 Y축 기준으로 뒤집고, -1이면 양방향 모두 뒤집음

- 전치 (행열치환)

  • cv2.transpose(src[, dst]) -> dst
    • 매개변수dst: 저장할 배열.  리턴 타입에 있는 dst를 리턴하는 결과인데 값 자체는 동일

- 복사

  • cv2.repeat(src, nx, ny[,dst]) ->dst
    • nx 와 ny는 반복 횟수

 

# 이미지 복사 및 회전

x_axis
y_axis
xy_axis
trans_image
rep_image

 

 

5-4) 이미지의 연산

- 이미지는 산술 연산은 거의 수행하지 않음

  • 덧셈을 하는 경우에 255를 초과하면 의미가 없기 때문
  • 뺄셈을 하는 경우에 음수가 나오면 무의미

- 이미지는 비트 연산을 많이 수행

  • 비트 연산을 이용해서 이미지 합성을 수행
  • bitwise_or(둘 다 0인 경우만 0이고 나머지는 1), 
    bitwise_and(둘 다 1인 경우만 1이고 나머지는 0), 
    bitwise_xor(같으면 0 다르면 1)
  • 0 과 0 또는 1 과 1 이면 0 그 이외의 경우는 1), bitwise_not(0->1, 1->0) 으로 연산
#2개의 배열 생성
image1 = np.zeros((300, 300), np.uint8)
image2 = image1.copy()

#중앙점의 좌표 찾기
h, w = image1.shape[:2]
cx, cy = w//2, h//2

#배열에 원과 사각형 그리기
cv2.circle(image1, (cx, cy), 100, 255, -1)
cv2.rectangle(image2, (0, 0, cx, h), 255, -1)
#or를 하게되면 색상이 있는 영역이 합쳐집니다.
image3 = cv2.bitwise_or(image1, image2)
#and를 하게되면 색상이 없는 영역이 합쳐집니다.
image4 = cv2.bitwise_and(image1, image2)
#xor를 하게되면 색상이 색상이 같은 영역은 색상이 제거되고 그렇지 않은 영역만 색상이 
#칠해집니다.
image5 = cv2.bitwise_xor(image1, image2)

cv2.imshow("image1", image1)
cv2.imshow("image2", image2)
cv2.imshow("image3", image3)
cv2.imshow("image4", image4)
cv2.imshow("image5", image5)

cv2.waitKey(0)
image1
image2
image3
image4

image5

 

 

5-5) 통계 함수 사용 가능 - 이미지 선명화

image = cv2.imread("./data/redpanda.png", cv2.IMREAD_GRAYSCALE)

(min_val, max_val, _, _) = cv2.minMaxLoc(image)

ratio = 255/(max_val - min_val)
dst = np.round((image - min_val) * ratio).astype('uint8')

cv2.imshow("image", image)
cv2.imshow("dst", dst)
cv2.waitKey(0)

 

'Python' 카테고리의 다른 글

[Python] 확률 분포 모형  (1) 2024.02.26
[Python] 기술통계  (0) 2024.02.22
[Python] 확률  (0) 2024.02.21
[Python] 데이터 스케일링  (1) 2024.02.16
[Python] 데이터 전처리  (0) 2024.02.15

[Python] 확률

0ㅑ채
|2024. 2. 21. 11:17

1.주피터 노트북에서 수학 기호 사용

- TeX 라는 조판 언어를 이용해서 수식을 표현할 수 있음 - Markdown Cell을 생성한 후 셀 안에서 $ 기호를 이용해서 수기을 표현
- 그리스 문자는 \그리스문자이름


2. 공통 코드

import pandas as pd
import numpy as np

#dist 플롯이나 histogram
import seaborn as sns 

#시각화에서 한글을 사용하기 위한 설정
import platform
from matplotlib import font_manager, rc


font_name = font_manager.FontProperties(fname="c:/Windows/Fonts/malgun.ttf").get_name()
rc('font', family=font_name)
    
#시각화에서 음수를 표현하기 위한 설정
import matplotlib
matplotlib.rcParams['axes.unicode_minus'] = False 

# Jupyter Notebook의 출력을 소수점 이하 3자리로 제한 
%precision 3

# precision은 소수점은 과학적 표기법으로 변환할 자릿수를 설정
# 아래와 같이 하면 소수점 셋째 자리 밑으로는 과학적 표기법으로 표시
pd.options.display.precision = 3

#경고 무시
import warnings
warnings.filterwarnings('ignore')

 

 

3. 집합

- 구별 가능한 객체의 모임 : 데이터 중복 불가
- 집합은 대문자로 표현, 원소는 소문자로 표현
- Python에서는 set과 frozenset 자료형으로 집합 표현

  • set: 내용 변경 가능한 mutable 자료형
  • frozenset: 내용 변경 불가한 immutable 자료형
    • dict의 key는 반드시 immutable만 가능하기 때문에 frozenset이다. 
    • dir(frozenset): set과 달리 'add' 가 없다. 수정 불가능하니까!

 

3-1) 집합 생성

# set 원소로 생성

a = {1, 2, 3, 1, 2}
print(a)
{1, 2, 3}

 

  • 중복이 자동 제거됨. 

 
# 빈 set 생성

b = {} # dict가 되므로 이렇게 만들면 안됨
print(type(b))

c = set()
print(type(c))
<class 'dict'>
<class 'set'>

 
#frozenset 생성

d = frozenset()
d.add()
AttributeError: 'frozenset' object has no attribute 'add'
  • immutable은 무조건 처음에 데이터를 가지고 만들어야 한다. 수정이 안되니까!
d = frozenset([1, 2, 3])
print(type(d))
<class 'frozenset'>

 
# 연산자 오버로딩
Overloading: 동일한 이름의 메소드가 2개 이상 존재하는 경우 

  • 원래는 함수이지만 연산자를 이용해서 호출할 수 있도록 작성
  • 대신 이 메소드는 매개변수의 개수나 자료형이 달라야 한다.
  • set은 |와 &연산자 가능 (__and__, __or__가 존재)
    원래 이렇게 쓰는 애는 아닌데, 클래스 안에서 재정의해서 다른 용도로 쓰게 하는 것을 연산자 오버로딩이라고 한다.
print({1, 2, 3} & {2, 3, 4})
print({1, 2, 3} | {2, 3, 4})
{2, 3} 
{1, 2, 3, 4}
#부등호도 가능 (__ne__, __lt__가 존재)
print({1, 2, 3} > {2, 3})
  • 부분 집합 여부를 부등호로 판정
True




 
 
 

3-2) 연산

- set에서 합집합, 교집합, 차집합을 구해주는 함수 제공

  • 합집합이나 교집합은 |나 &연산자로도 구할 수 있음

 
 
 

4. 확률

4-1) 확률의 정의와 의미

- 확률은 현실에서 해결하려는 문제와 결부해서 정의
- 어떤 문제가 어떤 답을 가질 수 있고, 답의 신뢰성이 얼마인지 계산하는 정량적 방법 제시

  • ex. 동전을 던졌을 때 앞면이 나올 것인가, 뒷면이 나올 것인가?

 

4-2) 확률에서 나오는 개념

- 확률 표본(random sample, probablistic sample):문제에서 발생할 수 있는 하나의 현상 혹은 선택될 수 있는 하나의 경우(표본)
- 표본 공간: 가능한 모든 표본의 집합   Ω

  • 표본 공간은 도메인 지식을 바탕으로 결정
    • ex. 동전던지기에서 앞면, 뒷면뿐만 아니라 동전이 세워지는 경우도 있다. 그런데 거의 일어나지 않기 때문에 표본 공간은 그냥 앞면, 뒷면만 설정한다.
  • 특별한 경우에는 표본공간의 개수가 무한대가 되기도 한다.
    • ex. 주식의 등락폭: -30% ~ 30%

- 시행: 조작
- 사건(event): 표본 공간의 부분 집합으로 우리가 관심을 가지는 일부 표본의 집합

  • 시행에서 얻을 수 있는 사건의 모든 집합을 전사상이라고 함
    • 동전 던지기에서 나올 수 있는 표본은 앞면과 뒷면
      전사상: {}, {앞면}, {뒷면}, {앞면, 뒷면}

- 확률: 모든 각각의 사건에 어떤 숫자(정량)를 할당하는 함수  P

  • 함수가 지켜야 할 규칙: 콜모고로프의 공리
  • 모든 사건에 대해 확률은 실수이고 0 또는 양수
  • 표본 공간이라는 사건에 대한 확률은 1
  • 공통 원소가 없는 두 사건의 합집합의 확률은 사건별 확률의 합

 

4-3) 확률을 바라보는 관점

- 빈도주의 관점

  • 반복적으로 선택된 표본이 사건 A의 원소가 될 경향을 사건의 확률이라고 본다. 
  • 확률이 0.5 : 10000번 시행했을 때 5000번이 나오는 경향
  • 반복했을 때 몇번 나오니?

 
- 베이지안 관점

  • 신뢰도를 의미한다.
  • 확률이 0.5 : 1번 시행했을 때 그 사건이 발생할 가능성이 50%다.

 
- 보는 각도에 따라 빈도주의 관점이 될 수 있고, 베이지안 관점이 될 수 있다. 

  • 의사가 나에게 병이 걸렸을 가능성이 70%라고 진단을 내렸다.
  • 의사 입장에서는 여러 명의 환자를 진찰하기 때문에 10명이 진료를 받았을 때 7명은 병에 걸린다는 의미. 
  • 환자 입장에서는 내가 병에 걸렸을 가능성이 70%라고 해석하게 된다.

 

4-4) 확률의 성질

- 발생할 수 없는 사건의 확률은 0
- 어떤 사건의 여집합 확률은  1- P
- 합집합은 각 사건의 확률의 합에서 두 사건의 교집합의 확률을 뺀 것
 

4-5) 결합 확률

- 사건 A와 사건 B가 동시에 발생할 확률
- 두 사건의 교집합

 

4-6) 주변 확률

- 개별 사건의 확률: 범인 찾기 문제

  • 전체 인원은 20명에서 남자는 12명, 여자는 8명
  • 남자는 머리가 긴 사람이 3명, 여자는 머리가 긴 사람이 7명일 때, 
  • 범인이 남자이고 머리가 길다면, 결합 확률은 남자이고 머리 긴 사람 3명이므로  3/20
  • 주변 확률: 남자일 확률은 12/30, 머리가 길 확률은 10/20

 

4-7) 조건부 확률

- B가 사실일 경우의 사건 A에 대한 확률이 사건 B에 대한 사건 A의 조건부 확률

- P(B|A): 가능도(likelihood)
- P(B): 정규화 상수(Normalizing Constant) 또는 증거(Evidence)라고 함
 

4-8) 확률을 구해주는 패키지 - pgmpy

- JointProbabilityDistribution(variables, cardinality, values)

  • 결합 확률 모형을 만드는 데 사용하는 클래스
  • variables: 확률 변수의 이름으로 문자열의 리스트로 정의. [문자열이 1개여도 list 사용해야 함!]
  • cardinality: 각 확률 변수의 표본 또는 사건의 개수 [리스트]
  • values: 확률 변수의 모든 표본에 대한 확률 값의 [리스트]

# 남자가 12명이고 여자가 8명일 때 독립 확률

from pgmpy.factors.discrete import JointProbabilityDistribution as JPD

px = JPD(['X'], [2], np.array([12, 8]) / 20) #[12/20, 12/8]
print(px)
  • 남자와 여자는 상호 배타적인 사건이라서 하나의 확률 변수 ['X']
  • 사건의 개수 [2]
+------+--------+
| X    |   P(X) |
+===+=====+
| X(0) | 0.6000 |
+------+--------+
| X(1) | 0.4000 |
+------+--------+

 
# 결합 확률: 확률 변수가 2개 이상인 경우

  • 남자 12명이고 머리가 짧은 사람 9명, 긴 사람 3명
  • 여자 8명이고 머리가 짧은 사람 1명, 긴 사람 7명
    • 남자와 여자는 배타적이지만, 성별과 머리길이는 배타적이지 않으므로 확률 변수가 2개 ( X, Y ) 필요
    • 각 확률변수의 사건의 개수는 긻다,짧다로 2개 
    • 확률 값은 9, 3, 1, 7
pxy = JPD(['X', 'Y'], [2, 2], np.array([3, 9, 7, 1]) / 20)
print(pxy)
  • X: 남자, 여자
  • Y: 머리 짧다, 길다
+------+------+----------+
| X    | Y    |   P(X,Y) |
+===+===+=======+
| X(0) | Y(0) |   0.1500 |
+------+------+----------+
| X(0) | Y(1) |   0.4500 |
+------+------+----------+
| X(1) | Y(0) |   0.3500 |
+------+------+----------+
| X(1) | Y(1) |   0.0500 |
+------+------+----------+

 
 
- marginal_distribution(values, inplace=True)

  • 주변 확률을 구해주는 함수
  • values: 주변 확률을 구할 확률 변수의 이름 [문자열 리스트]
  • inplace: 기본 값이 Ture
  • JPD 객체를 이용해서 호출

- marginalize(values, inplace=True)

  • values 어떤 확률 변수의 주변 확률을 구하기 위해서 없애줄 확률 변수의 이름 [리스트]

-conditional_distribution(values, inplace=True)

  • values 조건부 확률을 구할 확률 변수의 이름 문자열과 값을 묶은 튜플의 리스트
# x인수로 받은 확률 변수에 대한 주변 확률 분포
pmx = pxy.marginal_distribution(['X'], inplace=False)
print(pmx)

# 인수로 받은 확률 변수를 주변화(marginalize)하여 나머지 확률 변수에 대한 주변 확률 분포
pmx = pxy.marginalize(['Y'], inplace=False)
print(pmx)
+------+--------+
| X    |   P(X) |
+====+====+
| X(0) | 0.6000 |
+------+--------+
| X(1) | 0.4000 |
+------+--------+
+------+--------+
| X    |   P(X) |
+====+=====+
| X(0) | 0.6000 |
+------+--------+
| X(1) | 0.4000 |
+------+--------+

 
 
 

4-9) 베이즈 정리

  • 조건이 주어졌을 때 조건부 확률을 구하는 공식
  • 새로운 데이터가 주어지기 전에 이미 어느정도 확률 값을 알고 있을 때 이를 새로 수집한 데이터와 합쳐서 최종 결과에 반영
  • 데이터 개수가 부족할 때 유용
  • 데이터를 추가하는 상황에서 전체 데이터를 새로 분석할 필요 없이 이전 분석 결과에 새로 들어온 데이터를 합쳐서 업데이트만 수행하면 됨

- 베이즈 정리를 이용한 검사 시욕 문제

  • 병에 걸린 환자에게 시약을 테스트한 결과 99% 확률로 양성반응을 보인 경우, 병에 걸렸는지 알 수 없는 환자에게 시약을 테스트했을 때 양성이 나왔으면 실제 병에 걸렸을 경우는?
    => 환자가 실제로 병에 걸린 경우를 사건 D 라고 하면 병에 걸려있지 않은 경우는 사건 D의 여집합
    => 시약 테스트에서 양성 반응을 보이는 경우를 사건 S 라고 하면 음성 반응을 보이는 경우는 사건 S의 여집합
  • 추가 조사를 했는데 이 병에 걸린 사람은 전체 인구의 0.2%
    => 현재 주어진 확률 값은 병에 걸린 환자에게 시약을 테스트했을 때 양성 반응을 보이는 확률인데 병에 걸렸다는 것은 추가된 조건 혹은 정보이므로 이 확률은 P(S|D) 로 표기
  • 이 병에 걸리지 않은 사람에게 시약 검사를 하면 양성 반응이 나타날 확률 5%
    => 구해야 하는 값은 이것과 반대로 양성 반응을 보이는 환자가 병에 걸려있을 확률인데 이 때에는 양성 반응을 보인다라는 것이 추가된 정보이므로 이 확률은 P(D|S) 로 표기

P(D|S) = P(S|D)P(D) / P(S)

 
- 몬티홀 문제

  • 3개의 문 중에 1개의 문 뒤에 선물이 있고, 나머지 2개의 문에는 선물이 없음
  • 1개의 문을 열어서 선물이 없다는 걸 보여주면 현재 선택한 문을 바꿀 것인가?
  • 실제로 바꾸는 것이 바꾸지 않는 것보다 확률이 2배 높다.

문의 위치를 0, 1, 2로 표현

  • 선물이 있는 문 확률변수 C의 값은 0, 1, 2
  • 참가자가 선택한 문 확률변수 X의 값은 0, 1, 2
  • 진행자가 열어준 문 확률변수 H의 값은 0, 1, 2

참가자가 1번 문을 선택했고, 진행자가 2번 문을 열어 선물이 없음을 보여주면 이진 분류가 된다.

  • 선물은 0번 아니면 1번에 존재
  • 진행자가 어떤 문을 여는가 하는 것은 선물의 위치와 참가자의 선택에 따라 달라지게 된다. 따라서 독립적인 일이 아니라 영향을 받는 일이 된다.
    • 선물이 0번 문 뒤에 있고, 참가자가 1번을 선택하면 진행자는 2번 문을 열 수밖에 없다. 
    • 선물이 1번 문 뒤에 있고, 참가자가 1번을 선택하면 진행자는 0번 아니면 2번 문을 연다. 
  • 0번 문 뒤에 선물이 있을 확률
    (1/3) / ((1 * 1/3) + (1/2 + 1/3) + (0 * 1/3))
    => 2/3
  • 1번 문 뒤에 있을 확률
    (1 - 2/3)
    => 1/3

'Python' 카테고리의 다른 글

[Python] 기술통계  (0) 2024.02.22
[Python] 이미지 데이터 다루기  (0) 2024.02.21
[Python] 데이터 스케일링  (1) 2024.02.16
[Python] 데이터 전처리  (0) 2024.02.15
[Python] 데이터 시각화  (0) 2024.02.14

1. Standardization (표준화)

1-1) 개요

- 다변량 분석에서는 각 컬럼의 숫자 데이터의 크기가 상대적으로 달라서 분석 결과가 왜곡되는 현상 발생

  • 다변량 분석: 2개 이상의 컬럼을 같이 사용해서 수행하는 분석

- 상대적 크기 차이를 동일하게 맞추는 작업이 표준화 또는 스케일링

  • 일반적으로 0  ~ 1이나   -1 ~ 1로 생성
  • 방법을 달리하는 가장 큰 이유는 이상치 여부 때문

 

1-2) 표준값과 편차값

- 모든 값들의 표준 값 기준으로 차이를 구해서 비교
 

표준 값: (데이터 평균) /표준편차

  • 표준 값 평균은 0, 표준 편차는 1
  • 정규분포와 비교하기 쉽다.

 
편차 값: 표준값 * 10 + 50

  • 양수로 바꿔주기. 음수는 오타나 오류를 많이 발생하고 직관적이지 않기 때문.
  • 편차 값의 평균은 50, 표준 편차는 10

 
표준점수 해석

  • 표준점수 0.0(=편차치 50) 이상은 전체의 50%
  • 표준점수 1.0(=편차치 60) 이상은 전체의 15.866%
  • 표준점수 2.0(=편차치 70) 이상은 전체의 2.275%
  • 표준점수 3.0(=편차치 80) 이상은 전체의 0.13499%
  • 표준점수 4.0(=편차치 90) 이상은 전체의 0.00315%
  • 표준점수 5.0(=편차치 100) 이상은 전체의 0.00002%
students = pd.read_csv("data/student.csv", encoding='ms949', index_col ='이름')

 
# 한글 폰트

import matplotlib.pyplot as plt
import platform
from matplotlib import font_manager, rc

font_name = font_manager.FontProperties(fname="c:/Windows/Fonts/malgun.ttf").get_name()
rc('font', family=font_name)

 
# 음수 출력 설정

plt.rcParams['axes.unicode_minus'] = False

 
# 표준 값 구하기

#표준값과 편차값 구하기
kormean, korstd = students['국어'].mean(), students['국어'].std()
engmean, engstd = students['영어'].mean(), students['영어'].std()
matmean, matstd = students['수학'].mean(), students['수학'].std()

students['국어표준값'] = (students['국어'] - kormean)/korstd
students['영어표준값'] = (students['영어'] - engmean)/engstd
students['수학표준값'] = (students['수학'] - matmean)/matstd

students['국어편차값'] = students['국어표준값'] * 10 + 50
students['영어편차값'] = students['영어표준값'] * 10 + 50
students['수학편차값'] = students['수학표준값'] * 10 + 50
students[['국어편차값','영어편차값','수학편차값']].plot(kind='bar')

 
 
 

1-3) scikit-learn의 표준화 클래스

StandardScaler(데이터)

  • 평균이 0, 표준편차가 1이 되도록 변환
  • 주성분 분석 등에서 많이 사용
  • 표준화된 유사 형태의 데이터 분포로 반환

*주성분 분석: 여러 독립변수의 주된 특성만 선별하여 독립변수의 개수(=차원의 수)를 줄일 수 있다.

 

MinMaxScaler(데이터)

  • 특정 값에 집중되어 있는 데이터일수록 표준편차에 의한 스케일 변화 값이 커진다. 
  • 한쪽으로 쏠린 데이터 분포는 형태가 거의 유지된 채 범위 값이 조절된다. 
  • 최대값이 1, 최소값이 0이 되도록 변환
  • (데이터 - 최소값) / (최대값 - 최소값)
  • 신경망(딥러닝)에서 주로 사용

 

RobustScaler(데이터)

  • 중앙값 0, IQR이 1이 되도록 변환
  • 데이터의 이상치가 많으면 평균과 표준편차에 영향을 미치기 때문에 표준화에 부정적
  • 중간값과 사분위 범위를 사용하여 스케일을 조정
  • (데이터 - 중간값) / (75%값 - 25%값)
  • => 이상치에 덜 민감해짐
  • 표준화된 유사 형태의 데이터 분포로 반환

 

- MaxAbsScaler(데이터)

  • 0을 기준으로 절대값이 가장 큰 수가 1 또는 -1이 되도록 변환
  • 절대값이 0 ~ 1 사이가 되도록 매핑
  • 각 열 별로 절대값이 가장 큰 값으로 나누는 것
  • 양수 데이터로만 구성된 경우에는 MinMax와 유사하면 이상치에 민감
  • 음수 자체가 어떤 의미를 지닌 경우 사용

 

- QuantileTransformer

  • 데이터를 1000개의 분위로 나눠 0 ~ 1 사이에 고르게 분포시키는 방식
  • 이상치의 영향을 제거하기 어려운 경우 사용. 이상치의 영향을 덜 받게 해줌
이상치를 제거하는게 맞을까, 영향을 줄이는 게 맞을까, 이상치끼리 분석해주는게 맞을까?
어쩔 수 없이 이상치를 가지고 분석해야 하는 경우 이렇게 스케일링을 해줘야 한다. 
그래서 먼저 데이터를 그래프로 그려보고 박스플롯이나 분산차트 같은 걸로 이상치를 확인한다.

전처리는 scikit-learn으로 거의 다 됨

스케일링 시 Feature 별로 크기를 유사하게 만드는 것은 중요하지만, 특성에 따라 어떤 항목은 데이터의 분포를 유지해야 한다. 데이터가 한 곳에 집중된 Feature를 표준화시키면 작은 단위 변화가 큰 차이를 나타내는 것처럼 반영되기 때문이다. 

 
 
- 표준화 과정

  • 데이터를 입력해서 fit 메소드 호출 - 분포 모수를 객체 내에 저장
  • 데이터를 입력해서 transform 메소드 호출 - 표준화된 결과 리턴
    • 2개의 과정을 합친 fit_transform 메소드도 있다.
  • 훈련 세트와 테스트 세트로 나눈 경우 동일한 방식으로 표준화 작업
from sklearn import preprocessing
x = mpg[['horsepower']].values

#표준화 전의 기술통계 값
print('평균:', np.mean(x))
print('표준 편차:', np.std(x))
print('최대값:', np.max(x))
print('최소값:', np.min(x))
평균: 104.46938775510205
표준 편차: 38.44203271442593
최대값: 230.0
최소값: 46.0

 
#평균 0, 표준편차 1로 만드는 스케일링 - StandardScaler

scaler = preprocessing.StandardScaler()
scaler.fit(x)
x_scaled = scaler.transform(x)

print('평균:', np.mean(x_scaled))
print('표준 편차:', np.std(x_scaled))
print('최대값:', np.max(x_scaled))
print('최소값:', np.min(x_scaled))
평균: -1.812609019796174e-16
표준 편차: 0.9999999999999998
최대값: 3.2654519904664348
최소값: -1.5209754434541274

 
# 이상치가 많은 경우 - RobustScaler

scaler = preprocessing.RobustScaler()
scaler.fit(x)
x_scaled = scaler.transform(x)
평균: 0.2150860344137655
표준 편차: 0.7537653473416848
최대값: 2.676470588235294
최소값: -0.9313725490196079

 

표준화 전 표준화 후

 
 
 
 

2. Normalization (정규화)

- 데이터 범위를 0과 1로 변환해서 데이터 분포 조정
- scikit-learn의 Normalizer 클래스의 객체를 만든 후 transform 함수 사용
- norm 옵션: L1, L2를 설정

  • L1: 맨해튼 거리
    • 각 좌표 거리의 합. 지도상에 보이는 경로 같은 거.
  • L2: 유클리드 거리
    • 각 좌표를 빼서 제곱하고 더한 다음에 루트 .. 그냥 대각선으로 가는 것

- 여러 개의 속성을 한꺼번에 스케일링
- 행 단위로 스케일링
- 여러 개의 컬럼을 가진 데이터(텍스트)에서 컬럼들이 독립적이지 않은 경우 이용
 

from sklearn.preprocessing import Normalizer
# 특성 행렬을 만듭니다.
features = np.array([[1, 2],
                     [2, 3],
                     [3, 8],
                     [4, 2],
                     [7, 2]])

# 정규화 객체 생성
normalizer = Normalizer(norm="l1")

# 특성 행렬을 변환
features_l1_norm = normalizer.transform(features)
print(features_l1_norm)
[[0.33333333 0.66666667]
 [0.4        0.6       ]
 [0.27272727 0.72727273]
 [0.66666667 0.33333333]
 [0.77777778 0.22222222]]
  • l1은 행 데이터의 합을 가지고 각 데이터를 나눈 값: 총계 1
normalizer = Normalizer(norm="l2")
features_l2_norm = normalizer.transform(features)
print(features_l2_norm)
[[0.4472136  0.89442719]
 [0.5547002  0.83205029]
 [0.35112344 0.93632918]
 [0.89442719 0.4472136 ]
 [0.96152395 0.27472113]]
  • l2를 설정하면 각 데이터를 제곱해서 더한 값의 제곱근
# max
normalizer = Normalizer(norm = "max")
features_l1_norm = normalizer.transform(features)
print(features_l1_norm)
[[0.5        1.        ]
 [0.66666667 1.        ]
 [0.375      1.        ]
 [1.         0.5       ]
 [1.         0.28571429]]

 
 
 
 

3. 다항 특성과 교차항 특성

  • 다항: 제곱해나가는 것
  • 교차항: 곱하는 것

- 컬럼들을 제곱하고 곱해서 데이터 추가
- 특성과 타겟 사이에 비선형 관계가 존재하는 경우 다항 특성 생성
- scikit-learn의 PolynomialFeatures 클래스 이용

  • degree 옵션: 몇차 항까지 생성할 것인지 설정
  • include_bias 옵션: 첫번째 항으로 상수 1을 추가할지 여부 설정
  • interation_only 옵션: True를 설정하면 교차항 특성만 생성
  • 생성된 객체의 get_feature_names_out 함수를 이용하면 변환식 이름으로 반환
from sklearn.preprocessing import PolynomialFeatures
# 특성 행렬을 만든다.
features = np.array([[2, 3],
                     [1, 2],
                     [22, 6]])

# PolynomialFeatures 객체를 만든다.
polynomial_interaction = PolynomialFeatures(degree=2, include_bias=False)

# 다항 특성을 만든다.
result = polynomial_interaction.fit_transform(features)
print(result)
[[  2.   3.   4.   6.   9.]
 [  1.   2.   1.   2.   4.]
 [ 22.   6. 484. 132.  36.]]
polynomial_interaction = PolynomialFeatures(degree=2, include_bias=True, interaction_only=True)

result = polynomial_interaction.fit_transform(features)
print(result)
[[  1.   2.   3.   6.]
 [  1.   1.   2.   2.]
 [  1.  22.   6. 132.]]

 
 
 

4. 특성 변환

- 컬럼에 함수를 적용해서 데이터를 변경
- pandas의 apply 함수 이용
- API: sklean.FunctionTransformer,  sklean.ColumnTransformer 

  • FunctionTransformer: 모든 열에 동일한 함수 적용하고자 할 때 사용
    • = pandas.apply
  • ColumnTransformer: 각 열에 다른 함수 적용하고자 할 때 사용
    • 함수, 변환기, 컬럼 이름의 리스트로 이루어진 튜플의 list를 매개변수로 받음

- pandas는 데이터가 Series나 DataFrame
- scikit-learn은 데이터가 numpy의 ndarray, 특별한 경우가 아니면 2차원 배열
 

#함수를 적용해서 데이터 변경
from sklearn.preprocessing import FunctionTransformer

features = np.array([[1, 3], 
			[3, 6]])

def add_one(x:int) -> int:
    return x + 1

one_transformer = FunctionTransformer(add_one)
result = one_transformer.transform(features)
print(result)
[[2 4]
 [4 7]]
df = pd.DataFrame(features, columns = ['f1', 'f2'])
print(df.apply(add_one))
   f1  f2
0   2   4
1   4   7

 
 
 
 

5. 숫자 데이터의 이산화

- 데이터를 분석할 때 연속된 데이터는 일정한 구간으로 나눠서 분석하는 것이 효율적

  • ex. 가격, 비용, 효율, 나이 등
  • 연속적인 값을 일정한 수준이나 정도를 나타내는 이산적인 값으로 나타내어 구간별 차이를 드러나게 함

 

5-1) 구간 분할

- 연속형 데이터를 일정한 구간으로 나누고 각 구간을 범주형 이산 변수로 치환하는 과정 : binning (구간 분할)
- pandas의 cut 함수: 연속형 데이터를 여러 구간으로 분할해서 범주형 데이터로 변환가능
- 구간을 분할할 때 반드시 일정한 간격을 만들 필요는 없음

10대는 쪼개서 분류하는게 좋다.

 

df = pd.read_csv('./data/auto-mpg.csv', header=None)

# 열 이름을 설정
df.columns = ['mpg','cylinders','displacement','horsepower','weight',
              'acceleration','model year','origin','name'] 

df['horsepower'].replace('?', np.nan, inplace=True)      # '?'을 np.nan으로 변경
df.dropna(subset=['horsepower'], axis=0, inplace=True)   # 누락데이터 행을 삭제
df['horsepower'] = df['horsepower'].astype('float')      # 문자열을 실수형으로 변환

 
 
# 구간 분할

count, bin_dividers = np.histogram(df['horsepower'], bins=3)
print(count)
print(bin_dividers)
[257 103  32]
[ 46.         107.33333333      168.66666667 230.  ]
  • 모든 함수는 데이터를 하나만 리턴할 수 있다.
  • 튜플은 튜플을 리턴할 때 나누어서 할당이 가능 

 

# 3개의 bin에 이름 지정
bin_names = ['저출력', '보통출력', '고출력']

# pd.cut 함수로 각 데이터를 3개의 bin에 할당
df['hp_bin'] = pd.cut(x=df['horsepower'],     # 데이터 배열
                      bins=bin_dividers,      # 경계 값 리스트
                      labels=bin_names,       # bin 이름
                      include_lowest=True)    # 첫 경계값 포함 여부

df[['horsepower', 'hp_bin']].head(20)

 
 
- numpy. digitize 

  • 첫번째 데이터는 이산화할 데이터
  • bins = 이산화할 경계값 리스트
  • right = 경계값 포함 여부 설정

- sklearn. Binarizer

  • 생성할 때 하나의 경계값을 대입해서 생성하고 fit_transform을 호출하면
    경계값보다 작은 경우는 0, 그렇지 않은 경우는 1을 할당해서 리턴

- sklearn. KBinsDiscretizer

  • 첫번째 매개변수: 구간 개수
  • encode: ordinal(각 구간을 하나의 컬럼에 일련번호 형태 리턴), onehot(희소 행렬 리턴), onehot-dense(밀집 행렬 형태 리턴)
  • strategy: quantile(일정한 비율), uniform(구간 폭 일정)
  • bin_edges_: 각 구간의 값을 알고자 할 때 속성 확인
Label Encoding: 순서가 의미 있을 때 (0, 1, 2)
OneHot Encoding: 순서가 의미 없을 때
- Sparse Matrix (희소 행렬): 행렬 전체 반환
- Dense Matrix (밀집 행렬): 값의 위치만 반환

a = [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10]] 

pandas의 메소드들은 DataFrame이나 Series를 이용해서 작업하지만
Machine Learning 관련된 메소드들은 2차원 이상의 ndarray로 작업

 
 
# pandas.cut()

age = np.array([[13],
                [30],
                [67],
                [36],
                [64],
                [24]])

# 30을 기준으로 분할
result = np.digitize(age, bins=[30])
print(result)
[[0]
 [1]
 [1]
 [1]
 [1]
 [0]]
  • 30이 안되면 0 / 30 이상이면 1로 변환
  • bins에는 여러개의 데이터 설정이 가능
#0-19, 20-29, 30-63, 64이상의 구간으로 분할
result = np.digitize(age, bins=[20,30,64])
print(result)
[[0]
 [2]
 [3]
 [2]
 [3]
 [1]]
#0-20, 21-30, 31-64, 64초과 구간으로 분할
result = np.digitize(age, bins=[20,30,64], right=True)
print(result)
print()
[[0]
 [1]
 [3]
 [2]
 [2]
 [1]]

 
 
# Binarizer를 이용한 구간 분할

from sklearn.preprocessing import Binarizer

#threshold(임계값)
binarizer = Binarizer(threshold=30.0)
print(binarizer.transform(age))
  • 흑백 이미지 데이터에서 뚜렷한 구분을 위해 임계값 아래와 위를 구분
[[0]
 [0]
 [1]
 [1]
 [1]
 [0]]

 
 
# KBinsDiscretizer를 이용한 구간 분할

from sklearn.preprocessing import KBinsDiscretizer

#균등한 분포로 4분할
#ordinal: 라벨 인코딩 - 일련번호
kb = KBinsDiscretizer(4, encode='ordinal', strategy='quantile')
print(kb.fit_transform(age))
print()

#원핫인코딩을 해서 희소 행렬로 표현
kb = KBinsDiscretizer(4, encode='onehot', strategy='quantile')
print(kb.fit_transform(age))
print()

#원핫인코딩을 해서 밀집 행렬로 표현
kb = KBinsDiscretizer(4, encode='onehot-dense', strategy='quantile')
print(kb.fit_transform(age))
[[0.]
 [1.]
 [3.]
 [2.]
 [3.]
 [0.]]

  (0, 0) 1.0
  (1, 1) 1.0
  (2, 3) 1.0
  (3, 2) 1.0
  (4, 3) 1.0
  (5, 0) 1.0

[[1. 0. 0. 0.]
 [0. 1. 0. 0.]
 [0. 0. 0. 1.]
 [0. 0. 1. 0.]
 [0. 0. 0. 1.]
 [1. 0. 0. 0.]]

 
 

5-2) 군집 분석

- 2개 이상의 컬럼의 값으로 이산화
- 데이터 전처리 단계에서 군집 분석한 결과를 기존 데이터의 컬럼으로 추가해서 이산화 수행 가능
- 수집 -> 전처리 -> 분석     *순서는 언제든지 변경되고 나선형으로 수행될 수 있음
- k 개의 그룹이 있다는 것을 아는 경우에는 k-means clustering을 사용하여 비슷한 샘플을 그룹화 할 수 있음

from sklearn.cluster import KMeans
# 모의 특성 행렬을 생성
sample = np.array([[13, 30],
                [30, 40],
                [67, 44],
                [36, 24],
                [64, 37],
                [24, 46]])

# 데이터프레임을 생성
dataframe = pd.DataFrame(sample, columns=["feature_1", "feature_2"])
# k-평균 군집 모델을 생성
clusterer = KMeans(3, random_state=42)

# 모델을 훈련
clusterer.fit(sample)

# 그룹 소속을 예측
dataframe["group"] = clusterer.predict(sample)

 
 
 
 

6. 이상치 탐지

- Z-score 이용

  • 중앙값을 기준으로 표준 편차가 절대값 3 범위의 바깥 쪽에 위치하는 데이터를 이상치로 간주
  • 데이터가 12개 이하이면 이상치 감지 불가

- Z-score 보정

  • 편차의 절대값 범위를 3.5로 설정한 후 0.6745를 곱해서 사용

 
- IQR(3사분위수 - 1사분위수)

  • 1사분위수보다 1.5(IQR) 이상 작은 값이나 3사분위수보다 1.5(IQR) 이상 큰 값을 이상치로 간주
  • boxplot에서 수염 바깥쪽 데이터를 이상치로 간주

파란 점이 이상치

 
- 투표: 3개 모두 사용해보고 2개 이상에서 이상치로 간주되는 데이터를 이상치 판정
- 데이터의 특정 비율을 이상치로 간주 

  • scikit-learn의 covariance.EllipticEnvelope 클래스 이용
  • 잘 제시되지는 않는 방법

 
- 수식을 이용한 이상치 탐지

#이상치 탐지를 위한 데이터 생성
features = np.array([[10, 10, 7, 6, 4, 4, 3,3],
                     [20000, 3, 5, 9, 2, 2, 2, 2]])

 
 
# Z-score 이용

def outliers_z_score(ys):
    threshold = 3
    
    mean_y = np.mean(ys)
    print("평균:", mean_y)
    
    stdev_y = np.std(ys)
    print("표준 편차:", stdev_y)
    
    z_scores = [(y - mean_y) / stdev_y for y in ys]
    print("z_score:", z_scores)
    
    return np.where(np.abs(z_scores) > threshold)
평균: 1254.5

표준 편차: 4840.068065120572

z_score:
[array([-0.25712448, -0.25712448, -0.25774431, -0.25795092, -0.25836414, -0.25836414, -0.25857074, -0.25857074]), 
array([ 3.87298272, -0.25857074, -0.25815753, -0.25733109, -0.25877735, -0.25877735, -0.25877735, -0.25877735])]

(array([1], dtype=int64), array([0], dtype=int64))
  • 하지만 데이터가 12개 이하면 null 값이 뜬다.

 
 
# Z-score 보정 - 데이터 개수에 상관없이 이상치 감지

def outliers_modified_z_score(ys):
    threshold = 3.5
    
    #중앙값 구하기
    median_y = np.median(ys)
    
    #중위 절대편차 구함
    median_absolute_deviation_y = np.median([np.abs(y - median_y) for y in ys])
    
    #중위 절대 편차를 가지고 계산한 후 0.6745 곱하기
    #결과가 3.5보다 크면 이상치로 감지 
    modified_z_scores = [0.6745 * (y - median_y) / median_absolute_deviation_y
                         for y in ys]
    
    return np.where(np.abs(modified_z_scores) > threshold)
(array([1], dtype=int64), array([0], dtype=int64))
  • 데이터가 12개 이하여도 상관없음

 
 
# IQR 

def outliers_iqr(ys):
    
    #1사분위, 3사분위
    quartile_1, quartile_3 = np.percentile(ys, [25, 75])
    
    #IQR
    iqr = quartile_3 - quartile_1
    
    #하한과 상한 구하기
    lower_bound = quartile_1 - (iqr * 1.5)
    upper_bound = quartile_3 + (iqr * 1.5)
    
    print("하한값:", lower_bound)
    print("상한값:", upper_bound)
    
    return np.where((ys > upper_bound) | (ys < lower_bound))
    
    
features = np.array([[10, 10, 7, 6, -4900],
                     [20000, 3, 5, 9, 10]])

print(outliers_iqr(features))
하한값: -1.875
상한값: 17.125
(array([0, 1], dtype=int64), array([4, 0], dtype=int64))
print(outliers_iqr(df['horsepower'].values))
하한값: -1.5
상한값: 202.5
(array([  6,   7,   8,  13,  25,  27,  66,  93,  94, 115], dtype=int64),)

 
 
# 일정한 비율의 데이터를 이상치로 간주

  • 값보다는 비율을 이용해서 이상치 판정
  • ex. 국가장학금 지급할 때 특정한 소득 기준은 없고 상위 10%를 제외한 학생에게 장학금을 지급하는 경우
#일정한 비율을 이상치로 간주하는 API
from sklearn.covariance import EllipticEnvelope
#데이터를 생성해주는 API
from sklearn.datasets import make_blobs
# 모의 데이터를 만듭니다.
features, _ = make_blobs(n_samples = 10,
                         n_features = 2,
                         centers = 1,
                         random_state = 1)
[[-1.83198811  3.52863145]
 [-2.76017908  5.55121358]
 [-1.61734616  4.98930508]
 [-0.52579046  3.3065986 ]
 [ 0.08525186  3.64528297]
 [-0.79415228  2.10495117]
 [-1.34052081  4.15711949]
 [-1.98197711  4.02243551]
 [-2.18773166  3.33352125]
 [-0.19745197  2.34634916]]

 

# 첫 번째 샘플을 극단적인 값으로 바꾼다.
features[0,0] = 10000
features[0,1] = 10000
print(features)

# 이상치 감지 객체 - 10%는 이상치로 간주
outlier_detector = EllipticEnvelope(contamination=0.1)

# 감지 객체를 훈련
outlier_detector.fit(features)

# 이상치를 예측 - (-1)이면 이상치, (1)이면 정상
outlier_detector.predict(features)
[[ 1.00000000e+04  1.00000000e+04]
 [-2.76017908e+00  5.55121358e+00]
 [-1.61734616e+00  4.98930508e+00]
 [-5.25790464e-01  3.30659860e+00]
 [ 8.52518583e-02  3.64528297e+00]
 [-7.94152277e-01  2.10495117e+00]
 [-1.34052081e+00  4.15711949e+00]
 [-1.98197711e+00  4.02243551e+00]
 [-2.18773166e+00  3.33352125e+00]
 [-1.97451969e-01  2.34634916e+00]]

array([-1,  1,  1,  1,  1,  1,  1,  1,  1,  1])
 
 
 
 
 

 
# 연습문제

log.txt
0.05MB

 

  • 파일은 공백으로 구분
  • 첫번째 데이터는 접속한 컴퓨터의 IP, 마지막 데이터는 트래픽

전체 트래픽의 합계 구하기
IP별 트래픽의 합계 구하기
 

더보기

#전체 트래픽의 합계 구하기

# 파일 불러오기
with open('./data/log.txt', 'r') as f:
    data = f.readlines()

# 공백 기준으로 데이터 분리하기
data_split = [x.strip().split() for x in data[1:]]

# 데이터프레임 생성, 열 이름 지정
df = pd.DataFrame(data_split, columns=['IP', 'x', 'x', 'datetime', 'um', 'GET/POST', 'path', 'port', 'server', 'traffic'])

# 필요없는 컬럼 제거
df = df.drop('x', axis=1)

# 트래픽 계산에 방해되는 놈들 제거
df['traffic'].replace('-', np.nan, inplace=True)
df['traffic'].replace('"-"', np.nan, inplace=True)
df = df.dropna()

# 트래픽 정수형 형변환
df.traffic = df.traffic.astype('int')

# 전체 트래픽 합계
nan.traffic.sum()

 

 

# IP별 트래픽의 합계 구하기

#IP별로 그룹화
grouped = df.groupby(['IP'])

ip_sum = grouped.sum()

 
=> Comprehension

더보기

Comprehension

r = []
for i in L:
    r.append(i**2)
print(r)

#map 
r = list(map(lambda x:x**2, L))
print(r)

#list comprehension
r = [i**2 for i in L]
print(r)

#필터링 : L의 모든 데이터를 i에 순차적으로 대입하고 조건문이 참인 경우만 list 생성
r = [i for i in L if i % 2 == 0]
print(r)
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
[0, 2, 4, 6, 8]
li1 = [1, 2, 3]
li2 = [1, 2, 3]

#for 안에 for 사용
#먼저 나온 for가 바깥쪽, 뒤에 나온 for가 안쪽
r = [x * y for x in li1 for y in li2]
print(r)
[1, 2, 3, 2, 4, 6, 3, 6, 9]

 
 
 

 

 


 

7. 텍스트 데이터

7-1) 한글 형태소 분석

형태소 분석기 설치 - konlpy
- JDK(자바 개발 도구)를 설치
  JAVA_HOME 환경 변수에 JDK 경로를 설정
  PATH에 JDK의 bin 디렉토리 경로를 설정: Java 명령어를 콘솔에서 편리하게 사용하기 위해서
- Windows 의 경우에는 Visual C++ 재배포 패키지 설치
   Visual Studio 의 데스크톱 애플리케이션 개발 을 설치해도 됨
- Jpype-1 패키지 설치
   Python 3.11: pip install Jpype1-py3
   Python 3.12: pip install Jpype1
 
- 형태소 분석을 하는 이유

  • 우리나라는 공백 단위로 분할을 해서는 단어를 만들 수 없기 때문에 공백 단위로 쪼개고 어간 추출 필요
  • 데이터 분석을 하다보면 특정 품사의 데이터만 필요한 경우가 발생

 
- 한글 형태소 분석기 모듈

  • Hannanum
  • Kkma
  • Komoran
  • Mecab
  • Okt
  • Twitter (최근에는 사용을 권장하지 않음)
#형태소 분석할 문장
text = "태양계는 지금으로부터 약 46억년 전 거대한 분자구름의 일부분이 중력붕괴를 일으키면서 형성되었다."

#자연어 처리를 할 때 공백 단위로 토큰화 하는 것은 큰 의미가 없음
#공백 단위로 토큰화하는 것은 로그 분석을 할 때 입니다.
data = text.split(' ')
print(data)
['태양계는', '지금으로부터', '약', '46억년', '전', '거대한', '분자구름의', '일부분이', '중력붕괴를', '일으키면서', '형성되었다.']

 
 

from konlpy.tag import Kkma
kkma = Kkma()
#문장 분석 - 하나의 텍스트에 마침표가 있는 경우 분할
print(kkma.sentences(text))
#단어별 분석 - 명사만 추출
print(kkma.nouns(text))
#형태소 분석 - 품사를 같이 제공: 이 작업을 수행하는 것을 품사 태깅 - 자연어 처리의 한 분야
print(kkma.pos(text))
['태양계는 지금으로부터 약 46억 년 전 거대한 분자 구름의 일부분이 중력 붕괴를 일으키면서 형성되었다.']

['태양계', '지금', '46', '46억년', '억', '년', '전', '거대', '분자', '분자구름', '구름', '일부분', '중력', '중력붕괴', '붕괴', '형성']

[('태양계', 'NNG'), ('는', 'JX'), ('지금', 'NNG'), ('으로', 'JKM'), ('부터', 'JX'), ('약', 'MDN'), ('46', 'NR'), ('억', 'NR'), ('년', 'NNB'), ('전', 'NNG'), ('거대', 'NNG'), ('하', 'XSV'), ('ㄴ', 'ETD'), ('분자', 'NNG'), ('구름', 'NNG'), ('의', 'JKG'), ('일부분', 'NNG'), ('이', 'JKS'), ('중력', 'NNG'), ('붕괴', 'NNG'), ('를', 'JKO'), ('일으키', 'VV'), ('면서', 'ECE'), ('형성', 'NNG'), ('되', 'XSV'), ('었', 'EPT'), ('다', 'EFN'), ('.', 'SF')]

 
 

from konlpy.tag import Hannanum

hannanum = Hannanum()
print(hannanum.nouns(text))
#사용하는 단어 사전이 다르므로 앞의 결과와 다른 결과가 나올 수 있습니다.
print(hannanum.pos(text))

from konlpy.tag import Okt
['태양계', '지금', '약', '46억년', '전', '거대', '분자구름', '일부분', '중력붕괴', '형성']

[('태양계', 'N'), ('는', 'J'), ('지금', 'N'), ('으로부터', 'J'), ('약', 'N'), ('46억년', 'N'), ('전', 'N'), ('거대', 'N'), ('하', 'X'), ('ㄴ', 'E'), ('분자구름', 'N'), ('의', 'J'), ('일부분', 'N'), ('이', 'J'), ('중력붕괴', 'N'), ('를', 'J'), ('일으키', 'P'), ('면서', 'E'), ('형성', 'N'), ('되', 'X'), ('었다', 'E'), ('.', 'S')]

 
 
 

7-2) BoW (Bag of Word)

- 텍스트 데이터에서 특정 단어의 등장 횟수를 나타내는 특성을 만드는 작업
- 워드클라우드처럼 등장 횟수를 이용해서 시각화를 위해서 수행하고너 단어별 가중치를 적용하기 위해서 작업을 수행
- scikit-learn 의 CountVectorizer 클래스를 이용해서 인스턴스를 생성하고 fit_transform 함수에 문자열을 대입하면 BoW 특성 행렬을 만들어주고 to_array 함수를 호출하면 밀집 행렬로 결과를 리턴합니다.
 
- 인스턴스를 생성할 때 사용할 수 있는 옵션

  • ngram_range: 정수 형태의 튜플을 대입하면 ngram(단어가 연속으로 등장하는 경우 하나의 단어로 취급) 옵션 설정 가능
  • stop_words: 불용어 설정
  • vocabulary: 횟수를 계산할 단어의 list
  • max_df: 최대 개수
  • min_df: 최소 개수
  • max_feature: 상위 몇 개의 단어만 추출

 
- 인스턴스의 속성: get_feature_names()를 호출하면 각 특성에 연결된 단어를 확인
- 단어 별 빈도수 확인

#샘플 데이터 생성
import numpy as np
import pandas as pd

text_data = np.array(['I love Newziland newziland', 
                     'Sweden is best',
                     'Korea is not bad',
                     'Python Python Java Python R Java C'])

from sklearn.feature_extraction.text import CountVectorizer

#BoW 객체를 생성
#모든 문자열을 소문자로 변경해서 수행
#알파벳 한글자는 모두 제거
countVectorizer = CountVectorizer()
bag_of_words = countVectorizer.fit_transform(text_data)

#희소 행렬
print(bag_of_words)
#밀집 행렬
print(bag_of_words.toarray())
#각 열의 의미
print(countVectorizer.get_feature_names_out())
[[0]
 [0]
 [0]
 [0]]
['newziland, korea']

 
 
 

7-3) 단어 별 가중치 적용

tf-idf(term frequency - inverse document frequency)를 이용한 가중치 계산
가중치를 계산할 때는 tf * idf

  • idf = log((1 + 문서의개수)/1+등장한문서빈도) + 1
    • 여러 문서에서 등장하는 단어가 있다면 이 단어는 중요하지 않을 가능성이 높음
  • tf = 하나의 문서에서 등장한 빈도 수
    • 하나의 문서에 어떤 단어가 많이 등장할수록 중요한 단어

 

text_data = np.array(['I love Newziland newziland', 
                     'Sweden is best',
                     'Korea is not bad',
                     'Python Python Java Python R Java C'])

#단어별 가중치 확인
from sklearn.feature_extraction.text import TfidfVectorizer

tfidfVectorizer = TfidfVectorizer()
feature_matrix = tfidfVectorizer.fit_transform(text_data)
print(tfidfVectorizer.vocabulary_)
print(feature_matrix.toarray())

 

{'love': 5, 'newziland': 6, 'sweden': 9, 'is': 2, 'best': 1, 'korea': 4, 'not': 7, 'bad': 0, 'python': 8, 'java': 3}
[[0.         0.         0.         0.         0.         0.4472136
  0.89442719 0.         0.         0.        ]
 [0.         0.61761437 0.48693426 0.         0.         0.
  0.         0.         0.         0.61761437]
 [0.52547275 0.         0.41428875 0.         0.52547275 0.
  0.         0.52547275 0.         0.        ]
 [0.         0.         0.         0.5547002  0.         0.
  0.         0.         0.83205029 0.        ]]

 
 
 
 

8. 시계열 데이터

- 날짜와 시간을 이용해서 정렬된 데이터
- 일정한 패턴을 가진 데이터도 시계열 데이터로 간주
- 물리적 흔적: 의학, 청각학 또는 기상학 등에서 측정한 물리적 흔적
 
 

8-1) pandas의 시계열 자료형

- datetime64(TimeStamp)

  • 시계열 데이터를 저장하기 위한 자료형
  • tz 옵션: 시간대 설정 가능
  • 부등호 이용해서 크기 비교 가능
  • 뺄셈 수행해서 간격 확인 가능

- Period

  • 두 시점 사이의 일정한 기간을 나타내는 자료형
  • 데이터를 생성할 때 많이 이용

 
 

8-2) 자료형 변환

- 문자열 데이터를 datetime64로 변환

  • pandas.to_datetime 함수 이용
  • 날짜 형식의 문자열과 format 매개변수에 날짜 형식을 지정해서 생성하는데 format을 생략하면 유추
  • errors 매개변수에 ignore를 설정하면 에러가 발생했을 때 문자열을 그대로 리턴, coerce를 설정하면 문제가 발생할 때 NaT를 설정하며 raise를 설정하면 예외를 발생시켜서 중단

 

df = pd.read_csv("./data/stock-data.csv")
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 20 entries, 0 to 19
Data columns (total 6 columns):
 #   Column  Non-Null Count  Dtype 
---  ------  --------------  ----- 
 0   Date    20 non-null     object
 1   Close   20 non-null     int64 
 2   Start   20 non-null     int64 
 3   High    20 non-null     int64 
 4   Low     20 non-null     int64 
 5   Volume  20 non-null     int64 
dtypes: int64(5), object(1)
memory usage: 1.1+ KB
  • 현재는 Date 컬럼이 object - 문자열
df['NewDate'] = pd.to_datetime(df['Date'])
df.info()
...
 0   Date     20 non-null     object 
...
  • 데이터를 출력해보면 Date 와 NewDate는 구분이 안되지만 자료형을 확인하면 다름
#데이터를 출력해보면 Date 와 NewDate는 구분이 안되지만 자료형을 확인하면 다름
print(df.head())
#연속된 날짜는 인덱스로 많이 사용
df.set_index('NewDate', inplace=True)
print(df.head())





 
 
 

8-3) Period

- to_period 함수를 이용해서 Timestamp 객체를 일정한 기간을 나타내는 Period 객체로 변환 가능

  • freq 옵션: 기준이 되는 기간 설정

dates = ['2017-01-01', '2018-06-03', '2019-11-02']
#문자열을 Timestamp로 변경
pddates = pd.to_datetime(dates)
print(pddates)
DatetimeIndex(['2017-01-01', '2018-06-03', '2019-11-02'], dtype='datetime64[ns]', freq=None)
#월로 변경 - 모든 날짜가 월 초 나 월 말로 변경
pr_months = pddates.to_period(freq='M')
print(pr_months)
PeriodIndex(['2017-01', '2018-06', '2019-11'], dtype='period[M]', freq='M')
#분기로 변경
pr_months = pddates.to_period(freq='Q')
print(pr_months)
PeriodIndex(['2017Q1', '2018Q2', '2019Q4'], dtype='period[Q-DEC]', freq='Q-DEC')

 
 
 

8-4) date_range

- 일정한 간격을 맞는 시계열 데이터 생성 함수
- 매개변수

  • start: 시작 날짜
  • end: 종료 날짜
  • periods: 생성할 데이터 개수
  • freq: 간격
  • tz: 시간대 설정('Asia/Seoul')

 
# 2024년 1월 1일 부터 일 단위로 30개의 데이터를 생성

ts_ms = pd.date_range(start='2024-01-01', end=None, periods=30, freq='D')
print(ts_ms)
DatetimeIndex(['2024-01-01', '2024-01-02', '2024-01-03', '2024-01-04',
               '2024-01-05', '2024-01-06', '2024-01-07', '2024-01-08',
               '2024-01-09', '2024-01-10', '2024-01-11', '2024-01-12',
               '2024-01-13', '2024-01-14', '2024-01-15', '2024-01-16',
               '2024-01-17', '2024-01-18', '2024-01-19', '2024-01-20',
               '2024-01-21', '2024-01-22', '2024-01-23', '2024-01-24',
               '2024-01-25', '2024-01-26', '2024-01-27', '2024-01-28',
               '2024-01-29', '2024-01-30'],
              dtype='datetime64[ns]', freq='D')

 
 
 

8-5) 날짜 데이터의 분리

- pandas 의 Series 의 dt.단위를 이용

  • year, month, day, hour, minute, weekday(월요일이 0) 를 이용

 
 

8-6) 시차 특성을 갖는 데이터 생성

- shift(간격, freq)를 이용하는데 이 함수는 pandas의 Series의 함수

df = pd.read_csv("./data/stock-data.csv")
df['NewDate'] = pd.to_datetime(df['Date'])
  • 년도만 추출해보면
print(df['NewDate'].dt.year)
0     2018
1     2018
2     2018
3     2018
...
Name: NewDate, dtype: int64
  • 이 경우는 간격이 하루 단위가 아니라서 앞의 데이터가 밀려들어오는 것처럼 보이지 않는데 전부 하루 간격이라면 밀려들어온다.
print(ts.shift(-1))
NewDate
2018-07-02    0.579450
2018-06-29   -0.774314
2018-06-28    0.650039
...

 
 
 

8-7) 결측치 처리

- 누락된 데이터의 기간을 삭제하거나
대체(Imputation)법: 관측에 기반에서 누락된 데이터를 채워넣는 방법

  • 누락된 데이터의 이전이나 이후값으로 채워넣는 방식
  • 이동 평균으로 데이터 대체
  • 보간: 인접한 데이터를 기반으로 누락된 데이터를 추정해서 대입하는 방법
    이전과 이후 데이터를 가지고 예측을 해서 설정 - 선형으로 예측
    실제 작업을 할 때는 데이터의 분포를 확인을 해서 선형인지 비선형인지 결정
    • DataFrame.interpolate 함수 이용
    • quadratic: 비선형으로 채움 (값 기반), 기본은 선형으로 채움
    • time: 시간 기반으로 채움
    • limit_direction: 보간 방향 설정

 
# 결측치 채우기

time_index = pd.date_range("01/01/2024", periods=5, freq='M')
dataframe = pd.DataFrame(index = time_index)

#결측치를 포함하는 데이터를 생성
dataframe['Sales'] = [1, 4, 9, np.nan, 25 ]
dataframe
#결측치 앞값으로 채우기
dataframe.ffill()
#결측치 뒤값으로 채우기
dataframe.bfill()

#선형으로 예측
dataframe.interpolate()

#비선형으로 예측
dataframe.interpolate(method="quadratic")

  • 중간에 날짜가 비어 있는 경우에는 앞 뒤의 날짜를 확인해서 
    다음 날짜가 맞는지 아닌지 확인해서 값을 기반으로 할지 날짜를 기반으로 할지 결정
from datetime import datetime

dates = [datetime(2024, 1, 1),datetime(2024, 1, 2), datetime(2024, 1, 3),
        datetime(2024, 1, 6), datetime(2024, 1, 7)]
        
dataframe = pd.DataFrame(index = dates)
dataframe['Sales'] = [1, 2, 3, np.nan, 7]

dataframe.interpolate(method='time')

 
 

8-9) resampling

- 시계열의 빈도를 변환하는 것
다운 샘플링(데이터의 빈도를 줄이는 것 - 언더 샘플링)

  • 원본 데이터의 시간 단위가 실용적이지 않은 경우: 어떤 것을 너무 자주 측정하는 경우
    게시판의 글을 분석하기 위해서 로그 데이터를 읽을 때 시간 단위가 초까지 측정된 경우가 있다면 다운 샘플링을 고려
  • 계절 주기의 특정 부분에 집중해야 하는 경우: 하나의 특정 계절에만 초점을 맞춘 분석을 해야 하는 경우
  • 한 데이터를 낮은 빈도로 측정된 다른 데이터와 맞춰 주기 위해서

업 샘플링(데이터의 빈도를 늘리는 것 - 오버 샘플링)

  • 시계열이 불규칙적인 경우
  • 입력이 서로 다른 빈도로 샘플링된 상황에서 주기가 긴 데이터를 주기가 짧은 데이터에 맞추기 위해서

- resample(freq, how, fill_method, closed, label, kind)

  • freq: 주기를 설정하는 것으로 이전에 사용하던 방식도 가능하고 '문자열단위', Hour, Minute, Second 등을 이용해서 설정하는 것도 가능(Second(30))
  • how: 채우는 값으로 기본은 mean 이고 first, last, median, max, min 설정 가능
  • fill_method: 업 샘플링을 할 때 데이터를 채우는 방법으로 기본은 None리고 ffill 이나 bfill 설정 가능
  • limit: 채우는 개수
  • closed: 다운 샘플링을 할 때 왼쪽 과 오른쪽 어떤 값을 사용할 지 여부를 설정
  • label: 다운 샘플링을 할 때 왼쪽 과 오른쪽 어떤 인덱스를 사용할 지 여부를 설정
range = pd.date_range('2024-02-01', '2024-02-02', freq='2min')

#2분마다 20개 이므로 총 시간은 40분
df = pd.DataFrame(index=range)[:20]
df['price'] = np.random.randint(low=10, high=100, size=20)
df['amount'] = np.random.randint(low=1, high=5, size=20)
df
#다운 샘플링 - 시간 간격을 10분으로 조정: 4개의 데이터로 변경
print(df.price.resample('10T').first())
2024-02-01 00:00:00    71
2024-02-01 00:10:00    41
2024-02-01 00:20:00    69
2024-02-01 00:30:00    15
Freq: 10T, Name: price, dtype: int32
#시계열 데이터의 경우 시간별 집계를 수행하는 방법
print(df.price.resample('10T').sum())
2024-02-01 00:00:00    360
2024-02-01 00:10:00    145
2024-02-01 00:20:00    267
2024-02-01 00:30:00    296
Freq: 10T, Name: price, dtype: int32

 
 

8-10) 이동 시간 윈도우

- 일반적인 기술 통계는 전체 데이터를 기반으로 계산하는데, 일정한 개수의 데이터를 가지고 움직이면서 기술 통계를 계산하는 것을 의미


 
- 단순이동평균

  • 현재 위치에서 일정한 개수를 설정하고 그 이전 데이터와의 평균 계산
  • DataFrame.rolling 함수 이용
  • window: 데이터 개수 설정

- 지수이동평균

  • 최근의 데이터에 가중치를 두는 방식
  • 알파 = 1- span(기간)
  • 현재데이터 + (1-알파)이전데이터 + (1-알파)제곱 * 이전에 이전 데이터... / 1 + (1-알파) + (1-알파)제곱..
  • Dataframe.ewm 함수 이용
  • span 옵션: span 설정
#2024년 1월 1일 부터 월 단위로 5개의 데이터를 생성
time_index = pd.date_range("01/01/2024", periods=5, freq='M')

#시계열 DataFrame을 생성
dataframe = pd.DataFrame(index=time_index)

dataframe['Stock_Price'] = [1, 2, 3, 4, 5]
  • pandas에서는 인덱스가 시간인 경우 또는 일정한 패턴인 경우를 시계열로 판정
#단순 이동 평균 계산
print(dataframe.rolling(window=2).mean())

#지수 이동 평균
print(dataframe.ewm(span=2).mean())
            Stock_Price
2024-01-31          NaN
2024-02-29          1.5
2024-03-31          2.5
2024-04-30          3.5
2024-05-31          4.5

            Stock_Price
2024-01-31     1.000000
2024-02-29     1.750000
2024-03-31     2.615385
2024-04-30     3.550000
2024-05-31     4.520661

 
 

8-11) 평활

- 시계열 자료의 체계적인 움직임을 찾아내기 위해서 과거 자료의 불규칙적인 변동 제거
- 불규칙 변동의 제거를 이동평균을 이용해서 분석
- 이동평균은 결국 과거 자료의 추세를 전 기간 또는 특정 기간 별로 평균을 계산해서 미래 자료를 예측한다는 것
- 과거 자료들의 가중치를 동일하게 적용하면 단순이동평균법, 각 자료의 가중치를 다르게 적용하면 지수평활법
 
 

8-12) 계절성 데이터

- 데이터의 계절성은 특정 행동의 빈도가 안정적으로 반복해서 나타나는 것으로 동시에 여러 빈도가 다르게 발생하는 것
- 시계열 데이터 중에는 일일, 주간, 연간의 계절적인 변화를 갖는 경향이 있는 데이터가 있음
- 그래프를 이용해서 대략적인 계절성 요인을 확인
 
계절성 시계열

  • 일련의 동작이 정해진 기간동안 반복되는 시계열
  • 주기가 일정

순환성 시계열

  • 반복적인 동작이 있기는 하지만 기간이 일정하지않은 경우
  • 주식 시작 이나 화산의 주기가 대표적인 순환성 시계열

 
- 계절성을 파악할 때는 산점도(scatter) 보다는 선(plot) 그래프를 이용하는 것이 좋다.
 
 
 
 
 

'Python' 카테고리의 다른 글

[Python] 이미지 데이터 다루기  (0) 2024.02.21
[Python] 확률  (0) 2024.02.21
[Python] 데이터 전처리  (0) 2024.02.15
[Python] 데이터 시각화  (0) 2024.02.14
[Python] 데이터 탐색  (0) 2024.02.14

[Python] 데이터 전처리

0ㅑ채
|2024. 2. 15. 17:35

1. 중복 데이터 처리

- 하나의 데이터 셋에서 동일한 관측 값이 2개 이상 중복되는 경우 분석 결과를 왜곡할 수 있음
 

1) duplicated()

- 데이터의 중복 여부를 bool의 Series로 리턴
 

2) drop_duplicates()

- 매개변수를 대입하지 않으면 모든 컬럼의 값이 일치하는 경우 제거
- subset 옵션: [‘컬럼 이름’]이나 [[‘컬럼’, ‘이름’, ‘list’]]를 대입하면 중복을 제거하고 첫번째 데이터만 유지
- keep 옵션: 위의 경우에서 마지막 데이터를 유지
- inplace 옵션 : 원본에 직접 작업

pandas와 DataFrame은 원본을 복제해서 수정하고 리턴

실시간으로 대량 발생하는 데이터는 데이터베이스에 바로 저장 못함
데이터 -> csv 파일에 기록 (유효성 검사를 할 수 없어서 잘못된 데이터가 저장되는 경우 발생) -> 데이터베이스에 반영

 

3) 중복 데이터 제거 실습

df = pd.DataFrame([['안녕하세요', '반갑습니다', '헬로우', '키아 오라', '반갑습니다'], 
                  ['한국', '한국', '미국', '뉴질랜드', '한국']])

#행 열 전치
df = df.T
df.columns = ['인사말', '국가']
print(df)
     인사말              국가
0  안녕하세요        한국
1  반갑습니다        한국
2    헬로우            미국
3  키아 오라       뉴질랜드
4  안녕하세요        한국

 
# 모든 컬럼에서 중복된 경우를 확인

print(df.duplicated())
0    False
1    False
2    False
3    False
4     True
dtype: bool

 
# 하나의 컬럼에서 중복된  경우를 확인

print(df.duplicated(subset=['국가'])) #국가 컬럼의 값이 중복된 경우 확인
0    False
1     True
2    False
3    False
4     True
dtype: bool

 
# 중복 데이터 제거

print(df.drop_duplicates(subset=['국가']))
     인사말    국가
0  안녕하세요    한국
2    헬로우    미국
3  키아 오라  뉴질랜드
  • 기본적으로는 앞의 데이터를 보존하고 뒤의 중복값을 제거한다.
  • 마지막 데이터를 보존하려면 keep 옵션 사용
print(df.drop_duplicates(subset=['국가'], keep='last')) # 마지막 데이터 보존
     인사말    국가
2    헬로우    미국
3  키아 오라  뉴질랜드
4  반갑습니다    한국

 
 
 
 

2. 함수 적용

1) apply

- Series나 DataFrame에 사용할 수 있는 함수

  • Series: 각 요소에 적용해서 결과를 Series로 리턴
  • DataFrame: 기본적으로 열 단위로 대입하고 DataFrame으로 리턴. axis=1을 설정하면 행 단위로 적용
import seaborn as sns
titanic = sns.load_dataset('titanic')
titanic.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 15 columns):
 #   Column       Non-Null Count  Dtype   
---  ------       --------------  -----   
 0   survived     891 non-null    int64   
 1   pclass       891 non-null    int64   
 2   sex          891 non-null    object  
 3   age          714 non-null    float64 
 4   sibsp        891 non-null    int64   
 5   parch        891 non-null    int64   
 6   fare         891 non-null    float64 
 7   embarked     889 non-null    object  
 8   class        891 non-null    category
 9   who          891 non-null    object  
 10  adult_male   891 non-null    bool    
 11  deck         203 non-null    category
 12  embark_town  889 non-null    object  
 13  alive        891 non-null    object  
 14  alone        891 non-null    bool    
dtypes: bool(2), category(2), float64(2), int64(4), object(5)
memory usage: 80.7+ KB
df = titanic[['sex', 'age']]
print(df.head())
      sex   age
0    male  22.0
1  female  38.0
2  female  26.0
3  female  35.0
4    male  35.0

 
# 사용자 함수

# 실수 1개를 받아서 1을 더해서 리턴하는 함수
def plusone(data:float) -> float:
    return data + 1

# 함수를 만들어서 적용
df['age'] = df['age'].apply(plusone)
print(df.head())
      sex   age
0    male  23.0
1  female  39.0
2  female  27.0
3  female  36.0
4    male  36.0

 
# 람다 함수

# 람다 함수를 이용해서 위와 동일한 작업 수행
df['age'] = df['age'].apply(lambda data : data+1)

 
# 브로드캐스트

df['age'] = df['age'] + 1

 
# 열의 모든 내용 대문자로 변경

# 문자열 클래스에 대문자로 변경해주는 메소드가 있는지 확인
df['sex'] = df['sex'].apply(str.upper)
print(df.head())
       sex       age
0    MALE     25.0
1  FEMALE  41.0
2  FEMALE  29.0
3  FEMALE  38.0
4    MALE     38.0

 
# 주의 에러

df = df.apply(str.upper)
print(df.head())

TypeError: descriptor 'upper' for 'str' objects doesn't apply to a 'Series' object

  • 데이터 프레임에 apply를 사용하면 함수에 이 대입
  • 하나의 열에 str.upper를 적용한 것이므로 에러 발생
  • 데이터 프레임에 apply를 적용할 때는 Series를 매개변수로 받아서 처리하는 함수 필요
#Series를 받아서 각 데이터가 null인지 확인
def missing_value(series: pd.Series) -> pd.Series:
    return series.isnull()

result = df.apply(missing_value)
print(result.head())
     sex    age
0  False  False
1  False  False
2  False  False
3  False  False
4  False  False
*SQL에서 where name = null을 하면 데이터가 없다고 나온다. insull을 사용해야 한다.
* null을 연산하면 null이 된다. null and False면 null이다. 
  그래서 항상 if(!=null)을 넣어줬음.
* 프로그래밍 언어에서는 Φ 이런게 null
* 데이터분석은 코드를 끊어서 봐야 한다.

 
 

2) pipe

- DataFrame 객체를 함수에 매핑할 때 사용

  • 하나의 값으로 작업하는 함수를 대입하면 동일한 구조의 데이터를 리턴
    • 모든 요소에 함수를 적용해서 그 결과를 가지고 데이터를 다시 생성해서 리턴
  • 집계 함수를 대입하면 열 단위로 함수를 적용해서 Series 리턴
  • Series를 리턴하는 함수를 호출해서 집계하면 하나의 값을 리턴
#Series를 받아서 Nan 여부를 리턴하는 함수
def missing_value(x:pd.Series) -> pd.Series:
    return x.isnull()

#Series를 받아서 True의 개수를 리턴하는 함수
def missing_count(x:pd.Series) -> int:
    return missing_value(x).sum()

#DataFrame을 받아서 총 Nan의 개수를 리턴하는 함수
def total_number_missing(x:pd.DataFrame) -> int:
    return missing_count(x).sum()
  • dataframe을 직접 넣기보다는 series로 작업을 많이 함!

 

 

타이타닉

df = titanic[['age', 'embarked']]
print(df.info())
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 891 entries, 0 to 890
Data columns (total 2 columns):
 #   Column    Non-Null Count  Dtype  
---  ------    --------------  -----  
 0   age       714 non-null    float64
 1   embarked  889 non-null    object 
dtypes: float64(1), object(1)
memory usage: 14.1+ KB
None
print(df.pipe(missing_value))
       age  embarked
0    False     False
1    False     False
2    False     False
3    False     False
4    False     False
..     ...       ...
886  False     False
887  False     False
888   True     False
889  False     False
890  False     False

[891 rows x 2 columns]
print(df.pipe(missing_count))
age         177
embarked      2
dtype: int64
  • Series를 받아서 1개의 데이터(정수)를 리턴하는 함수 적용
  • Series를 리턴 - 각 열의 결과
print(df.pipe(total_number_missing))
  • DataFrame을 받아서 집계한 후 하나의 값을 리턴함수를 적용
  • 하나의 값을 리턴
179

 
 
 

3. 컬럼 재구성

1) 열 순서 변경  sorted()

- DataFrame[재구성할 열 이름의 list]

columns = list(titanic.columns.values) #원래는 ndarray
print(columns)
['survived', 'pclass', 'sex', 'age', 'sibsp', 'parch', 'fare', 'embarked', 'class', 'who', 'adult_male', 'deck', 'embark_town', 'alive', 'alone']

 
# 열 정렬 

titanic_sorted = titanic[sorted(columns)]
titanic_sorted

 

2) 날짜 컬럼 분할  split()

df = pd.read_excel('./data/주가데이터.xlsx')
print(df.info())
print(df.head())
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 20 entries, 0 to 19
Data columns (total 7 columns):
 #   Column  Non-Null Count  Dtype         
---  ------  --------------  -----         
 0   연월일     20 non-null     datetime64[ns]
 1   당일종가    20 non-null     int64         
 2   전일종가    20 non-null     int64         
 3   시가      20 non-null     int64         
 4   고가      20 non-null     int64         
 5   저가      20 non-null     int64         
 6   거래량     20 non-null     int64         
dtypes: datetime64[ns](1), int64(6)
memory usage: 1.2 KB


         연월일   당일종가  전일종가     시가     고가     저가     거래량
0 2018-07-02  10100         600        10850  10900  10000  137977
1 2018-06-29  10700         300        10550  10900   9990  170253
2 2018-06-28  10400         500        10900  10950  10150  155769
3 2018-06-27  10900         100        10800  11050  10500  133548
4 2018-06-26  10800         350        10900  11000  10700   63039

 
# - 기준으로 분할  str.split()

#날짜 컬럼 문자열로 변환
df['연월일'] = df['연월일'].astype('str')

dates = df['연월일'].str.split("-")
0    [2018, 07, 02]
1    [2018, 06, 29]
2    [2018, 06, 28]
3    [2018, 06, 27]
4    [2018, 06, 26]
df['연'] = dates.str.get(0)
df['월'] = dates.str.get(1)
df['일'] = dates.str.get(2)
          연월일   당일종가  전일종가     시가     고가     저가     거래량     연   월   일
0  2018-07-02  10100   600  10850  10900  10000  137977  2018  07  02
1  2018-06-29  10700   300  10550  10900   9990  170253  2018  06  29
2  2018-06-28  10400   500  10900  10950  10150  155769  2018  06  28
3  2018-06-27  10900   100  10800  11050  10500  133548  2018  06  27
4  2018-06-26  10800   350  10900  11000  10700   63039  2018  06  26

 
 
 
 

4. 필터링

- 조건에 맞는 데이터만 추출

titanic = sns.load_dataset('titanic')

 
# age가 10 ~ 19인 데이터만 추출

ages = titanic[(titanic['age'] >= 10) & (titanic['age'] <20)]
ages['age'].unique()
array([14. , 15. , 19. , 18. , 11. , 17. , 16. , 14.5, 12. , 10. , 13. ])

 
# age가 10세 미만이고 sex가 female인 데이터 중에서 age,sex,alone 컬럼만 추출  loc[ [ ] ]

condition = (titanic['age']<10) & (titanic['sex']== 'female')
result = titanic.loc[condition, ['age','sex','alone']]
     age     sex  alone
10   4.0  female  False
24   8.0  female  False
43   3.0  female  False
58   5.0  female  False
119  2.0  female  False

 
# sibsp가 3, 4, 5인 데이터 추출  isin()

#sibsp가 3, 4, 5인 데이터 추출
조건 = (titanic['sibsp'].isin([3, 4, 5]))
결과 = titanic.loc[조건, :]
print(결과['sibsp'].unique())
[3 4 5]

 
 
 
 

5. 데이터 결합

- 데이터가 분할된 경우 합치는 기능
-  join처럼 키를 가지고 옆으로 결합하거나, 동일한 컬럼이면 세로로 결합
 

1) concat

- 구성 형태와 속성이 동일하면 행 또는 열 방향으로 이어붙임
- Series끼리는 옆으로 합쳐 DataFrame 생성
- DataFrame끼리는 상하로 합쳐서 하나의 DataFrame으로 생성

  • 한쪽에만 존재하는 컬럼도 합쳐짐, 값은 Nan

- axis=1을 설정하면 옆으로 결합

  • 이 경우에는 join 옵션 사용
  • 기본은 outer,  inner 설정 가능
df1 = pd.DataFrame({'a': ['a0', 'a1', 'a2', 'a3'],
                    'b': ['b0', 'b1', 'b2', 'b3'],
                    'c': ['c0', 'c1', 'c2', 'c3']},
                    index=[0, 1, 2, 3])
 
df2 = pd.DataFrame({'a': ['a2', 'a3', 'a4', 'a5'],
                    'b': ['b2', 'b3', 'b4', 'b5'],
                    'c': ['c2', 'c3', 'c4', 'c5'],
                    'd': ['d2', 'd3', 'd4', 'd5']},
                    index=[2, 3, 4, 5])

 
#세로 결합

print(pd.concat([df1, df2]))
    a   b   c    d
0  a0  b0  c0  NaN
1  a1  b1  c1  NaN
2  a2  b2  c2  NaN
3  a3  b3  c3  NaN
2  a2  b2  c2   d2
3  a3  b3  c3   d3
4  a4  b4  c4   d4
5  a5  b5  c5   d5
  • concat 옵션이 없는 경우: Set 연산처럼 세로 방향으로 결합
  • 컬럼의 이름이 다른 경우는 NaN 값으로 컬럼을 생성해서 결합

#가로 결합

print(pd.concat([df1, df2], axis=1))
       a       b      c       a        b      d
0   a0      b0   c0      NaN  NaN  NaN
1   a1      b2   c2      NaN  NaN  NaN
2   a2      b2   c2       a4     b4     b4
3   a3      b3   c3       a5     b5     b5
4  NaN  NaN  NaN   a6     a6    a6
5  NaN  NaN  NaN   a7     a7     a7
  • 기본적으로 index를 기준으로 결합
  • Outer Join처럼 수행

# inner join

print(pd.concat([df1, df2], axis=1, join='inner'))
      a   b     c    a    b   d
2  a2  b2  c2  a4  b4  b4
3  a3  b3  c3  a5  b5  b5

 
 

2) append

python에서 append가 _append(프라이빗)으로 변경됐음

- 인덱스와 상관없이 컬럼 이름만으로 상하 결합
- 최신 버전에서 API 변경
 
 

3) combine_first

- 두 데이터 셋의 인덱스가 겹치는 경우 데이터 정렬 기능까지 제공

print(df1.combine_first(df2))
     a    b     c       d
0  a0  b0   c0    NaN
1  a1  b2   c2    NaN
2  a2  b2   c2     b4
3  a3  b3   c3     b5
4  a6  a6  NaN   a6
5  a7  a7  NaN   a7

 
 

4) merge

- 2개의 데이터프레임을 합치는 메소드
- 데이터프레임만 설정하면 동일한 이름의 컬럼을 가지고 JOIN 수행
- key 옵션: 컬럼 설정 가능
- left_on right_on 옵션: 컬럼의 이름이 다른 경우, 직접 컬럼 이름 설정
- how 옵션: inner, left, right, outer 설정으로 outer join 수행
- suffixes 옵션: 튜플로 2개의 문자열을 설정하면 동일한 이름이 있는 경우 _이름 추가

  • 생략하면 _x, _y

- sort 옵션: 기본적으로 key를 기준으로 정렬 수행. 정렬 하지 않으려면 sort  = False
 
 

5) join

- merge와 유사
-  기본적으로 행 인덱스를 키 사용
- 키 설정이 가능하고 how 옵션 존재
- merge는 pandas 함수, join은 DataFrame의 메소드

price = pd.read_excel('./data/stock price.xlsx')
valuation = pd.read_excel('./data/stock valuation.xlsx')

 
#기본 조인

  • 아무런 옵션이 없으면 동일한 컬럼으로 Join
  • 기본적으로 Inner Join 수행
print(pd.merge(price, valuation))
print(pd.merge(price, valuation, how='outer', on='id'))

 
# join하는 컬럼의 이름이 다를 때

print(pd.merge(price, valuation, how='right', left_on='stock_name', right_on='name'))
#기본적으로 index로 조인
price.index=price['id']
valuation.index = valuation['id']

#동일한 컬럼 이름 제거
price.drop(['id'], axis=1, inplace=True)
valuation.drop(['id'], axis=1, inplace=True)

print(price.join(valuation))

 
 
 
 
 

6. 그룹연산

1) 그룹화

- 어떤 기준에 따라 그룹별로 나누어서 관찰
- groupby 함수 이용, 컬럼 이름이나 컬럼 이름의 list를 대입해서 그룹화
- 그룹화 후 get_group(그룹이름)을 이용하면 그룹화된 개별 그룹의 데이터를 가져오는 것이 가능
- 그룹화한 데이터는 iteration(for 사용)이 가능

  • 2개의 데이터를 튜플로 리턴
  • (그룹의 이름, 그룹의 데이터)

# titanic 데이터를 class 별로 그룹화

df = titanic[['age', 'sex', 'class', 'fare', 'survived']]
grouped = df.groupby(['class'])
for key, group in grouped:
    print(key, group)

 
# 그룹 count

#count
for key, group in grouped:
    print(key, len(group))
('First',) 216
('Second',) 184
('Third',) 491

 
# 특정 그룹의 데이터 선택

group3 = grouped.get_group('Third')

 
# 2개 열로 그룹화

grouped = df.groupby(['class', 'sex'])
for key, group in grouped:
    print(key)
('First', 'female')
('First', 'male')
('Second', 'female')
('Second', 'male')
('Third', 'female')
('Third', 'male')
  • key 가 2개 항목의 튜프로 생성
group3m = grouped.get_group(('Third', 'male'))

 
 
 

2) 집계

- count(null 제외), size(null 포함), mean, std, min, max,
  quantile(q=0.25|0.5|0.75), sum, var, sem, describe, first, last, nth
 
#표준편차

df = titanic[['class', 'age']]
grouped = df.groupby(['class'])
std_all = grouped.std()
print(std_all)

 
- 사용자 정의 함수 적용: agg 함수 호출해서 넘겨주기

#데이터 모임을 가지고 하나의 값을 리턴하는 함수
def min_max(x):
    return x.max() - x.min()

#사용자정의 함수 집계
result = grouped.agg(min_max)
result

 
- 사용자 정의 함수 적용해서 데이터 변환 : transform 함수 호출

  • map: 하나의 데이터를 받아서 하나의 데이터를 리턴하는 함수 생성해서 데이터 묶음에 적용하면
             하나 하나의 데이터를 함수에 대입해서 호출한 후 결과를 묶어서 데이터의 묶음을 리턴하는 함수

Z-score: (값-평균) / 표준편차

def z_score(x):
    return (x-x.mean()) / x.std()
    
age_zscore = grouped.transform(z_score)
age_zscore
  • 음수는 평균보다 아래
  • 양수는 평균보다 위

 
 

3) 멀티 인덱스

- 인덱스가 1 Level이 아닌 여러 Level로 구성되는 것
- 그룹화를 할 때 하나의 컬럼이 아니라 여러 컬럼으로 하는 경우 멀티 인덱스 생성

df = titanic[['class', 'sex', 'age']]
grouped = df.groupby(['class', 'sex'])

for key in grouped:
    print(key)

...
gdf = grouped.mean()
gdf

 
# 특정 class 출력

gdf.loc['First']

 
# 특정 sex 출력

gdf.xs('male', level='sex')

 
# 특정 행 출력

gdf.loc[('First', 'female')]
age    85
Name: (First, female), dtype: int64

 
 
 

4) pivot table

- 데이터를 그룹화하기 위한 함수

- 멀티 인덱스는 한 방향에 인덱스가 여러 개 적용
- 피벗 테이블은 양 방향에 인덱스를 적용

df = titanic[['class', 'sex', 'age']]
result = pd.pivot_table(df, index='class', columns='sex', values='age',
                       aggfunc='sum')
result

 
 
 
 
 

7. 데이터 구조화

- stacked와 unstacked

  • stacked: 가로 방향으로 넓게 펼쳐진 데이터를 세로 방향으로 길게 만드는 작업 - 컬럼을 인덱스로 활용
  • unstacked: 세로 방향으로 길게 펼쳐진 데이터를 가로 방향으로 넓게 만드는 작업 - 인덱스를 다시 컬럼으로 활용

- stacked 작업을 할 때는 NaN 데이터를 어떻게 처리할 것인지 결정
 

 

1) stack()

- stacked 작업을 수행해주는 함수

mul_index = pd.MultiIndex.from_tuples([('cust_1', '2015'), ('cust_1', '2016'),
                                      ('cust_2', '2015'), ('cust_2', '2015')])

data = pd.DataFrame(data=np.arange(16).reshape(4,4), index=mul_index, 
                   columns=['prd_1', 'prd_2', 'prd_3', 'prd_4'], dtype='int')
print(data)
stacked = data.stack()
cust_1  2015  prd_1     0
              prd_2     1
              prd_3     2
              prd_4     3
        2016  prd_1     4
              prd_2     5
              prd_3     6
              prd_4     7
cust_2  2015  prd_1     8
              prd_2     9
              prd_3    10
              prd_4    11
        2016  prd_1    12
              prd_2    13
              prd_3    14
              prd_4    15
dtype: int32

 
 

2) unstack

- unstacked 작업을 수행해주는 함수

  • 멀티인덱스인 경우 어떤 
#첫 레벨의 인덱스가 컬럼이 됩니다. cust_1 > 2015,6 > prd
unstacked1 = stacked.unstack(level=0)
unstacked2 = stacked.unstack(level=1)

print(unstacked1)
print(unstacked2)

#다단으로 구성된 인덱스의 경우는 여러 번 unstack 가능
print(unstacked2.unstack())
            cust_1  cust_2
2015 prd_1       0       8
     prd_2       1       9
     prd_3       2      10
     prd_4       3      11
2016 prd_1       4      12
     prd_2       5      13
     prd_3       6      14
     prd_4       7      15

              2015  2016
cust_1 prd_1     0     4
       prd_2     1     5
       prd_3     2     6
       prd_4     3     7
cust_2 prd_1     8    12
       prd_2     9    13
       prd_3    10    14
       prd_4    11    15
        2015                    2016      
            
       prd_1 prd_2 prd_3 prd_4 prd_1 prd_2 prd_3 prd_4
cust_1     0     1     2     3     4     5     6     7
cust_2     8     9    10    11    12    13    14    15

 
 

 

 

8. 숫자 데이터 전처리

1) 단위 환산

- 하나의 데이터 셋에서 서로 다른 측정 단위를 사용하게 되면 전체 데이터의 일관성 측면에서 문제가 발생할 수 있기 때문에 측정 단위를 동일하게 맞춰야 함

  • mpg 데이터는 자동차에 대한 연비인데, 미국에서는 갤런당 마일을 사용하고 우리나라에서는 L당 km 사용
  • 이럴 때 단위환산이 편리하다.
  • 1마일은 1.60934 km, 1갤런은 3.7841 L
mpg = pd.read_csv('./data/auto-mpg.csv', header=None)
mpg.columns = ['mpg', 'cylinders', 'displacement', 'horsepower', 'weight', 'acceleration', 'model year', 'origin', 'name']
#갤런 당 마일을 리터 당 킬로미터로 변환하는 상수
mpg_to_kpi = 1.60934 / 3.7841
mpg['kpi'] = mpg['mpg'] * mpg_to_kpi

 
 

2) 자료형 변환

- 컬럼의 자료형은 dtypes 속성이나 info()를 이용해서 가능
- 엑셀 파일이나 csv를 읽다보면 숫자 데이터를 문자열로 읽어오는 경우가 있음

  • 엑셀 파일에서 천 단위 구분 기호가 있는 경우: thousand 옵션에 (,)를 설정해서 읽어오면 해결

- 숫자로 인식할 수 없는 데이터가 포함된 경우

  • 숫자로 인식할 수 없는 데이터를 숫자로 변환하거나 제거한 후 숫자로 변경
mpg.dtypes
mpg                    float64
cylinders             int64
displacement      float64
horsepower        object
weight                float64
acceleration       float64
model year        int64
origin                 int64
name                 object
dtype: object

 
# 숫자로 변환

mpg['horsepower'] = mpg['horsepower'].astype('float')
mpg.dtypes

ValueError: could not convert string to float: '?'
 
# 인식할 수 없는 데이터 확인

mpg['horsepower'].unique()

 
# Null로 치환하여 제거하고 숫자로 변환

#?를 NaN으로 치환
mpg['horsepower'].replace('?', np.nan, inplace=True)

#NaN 제거
mpg.dropna(subset=['horsepower'], inplace=True)

#실수 자료형으로 변경
mpg['horsepower'] = mpg['horsepower'].astype('float')

mpg.dtypes
...
horsepower      float64
...

 
- 자료형을 범주형으로 변경하고자 하면 category로 자료형 설정

  • origin은 국가를 의미하는 범주형 인데 숫자 자료형, Category나 String으로 변경해주는게 좋다.
  • origin의 데이터를 1 -> USA,  2-> EU,  3-> JAPAN으로 치환
  • replace에 dict를 대입하면 key를 찾아서 value로 치환
mpg['origin']
0      1
1      1
2      1
3      1
4      1
mpg['origin'].replace({1:'USA', 2:'EU', 3:'JAPAN'}, inplace=True)
mpg['origin'].unique()
mpg['origin'].head()
array(['USA', 'JAPAN', 'EU'], dtype=object)

0    USA
1    USA
2    USA
3    USA
4    USA
Name: origin, dtype: object

 

- model year 컬럼의 값을 범주형(category)로 치환

  • category: 특정 값만을 가져야 하는 자료형
  • 문자열이나 숫자 자료형은 원핫 인코딩이 안되는 경우가 발생할 수 있음
    • 원핫 인코딩: 범주형의 데이터를 숫자형으로 만들 때 각각의 값을 컬럼으로 만드는 건데 문자열이나 숫자형은 안될 수 있음
mpg['model year'] = mpg['model year'].astype('category')
mpg.info()
 6   model year    392 non-null    category

 

 

 

 

 

 

 

 # 서울시 구별 CCTV와 인구 관계 분석

 

 

[전처리] 서울시 구별 CCTV와 인구 관계 분석

1. 서울시 자치구별 CCTV 현황 데이터 가져오기 - http://data.seoul.go.kr/ 열린데이터광장 메인 데이터분류,데이터검색,데이터활용 data.seoul.go.kr - CCTV 검색 - xlsx 파일을 다운로드(파일 이름을 cctv로 수

yachae4910.tistory.com

 

 

# 지도 출력

 

[전처리] 지도 출력 Choropleth

https://github.com/southkorea/southkorea-maps 한국 지리정보 사이트 1. 데이터 읽어오기 state_unemployment = 'data/US_Unemployment_Oct2012.csv' state_data = pd.read_csv(state_unemployment) print(state_data.head()) State Unemployment 0 AL 7.1

yachae4910.tistory.com

 

 

 

# 서울시 범죄 현황 시각화

 

[전처리] 서울시 범죄 현황 시각화

- https://data.seoul.go.kr/ - 서울시 5대 점죄 발생현황 통계 데이터 - crime.txt 1. 데이터 가져오기 import pandas as pd import folium import json criminal = pd.read_csv('./data/crime.txt', sep='\t', thousands=',', encoding='utf-8') 2.

yachae4910.tistory.com

 

 

 

# Cartogram

 

 

[전처리] 인구 소멸 지역 Cartogram

- 국가 통계 포털에서 인구 정보 내려받기 http://kosis.kr/statHtml/statHtml.do?orgId=101&tblId=DT_1IN1509& KOSIS kosis.kr 기본코드 import pandas as pd import numpy as np import platform import matplotlib.pyplot as plt %matplotlib inline p

yachae4910.tistory.com

 

'Python' 카테고리의 다른 글

[Python] 확률  (0) 2024.02.21
[Python] 데이터 스케일링  (1) 2024.02.16
[Python] 데이터 시각화  (0) 2024.02.14
[Python] 데이터 탐색  (0) 2024.02.14
[Python] MySQL 데이터로 DataFrame 만들기  (0) 2024.02.14

 

요약

기술이 고도로 성장하던 시기에는 과학과 기술, 노력이 중시됐다. 
그러나 저성장의 성숙한 경제 시기로 접어든 요즘에는 기술만큼이나 예술과 감각, 개성과 다양성이 중요해졌다.

 

야마구치씨와 구스노키씨의 대화로 진행되는
형식을 따라 정리해보았다.

 

제로가 아니라 플러스를 원한다.

기술이 없다면 마이너스(-)지만, 일을 잘한다는 것은 플러스(+)를 만들어가는 것이다.

기술력이 상향평준화된 인력시장에서 이제 기술은 평균이다. 평균점에 돈을 지불하는 사람은 없을 것이다.

 
 

구스노키

"만두를 만들 줄 안다는 이유로 누군가 선택된다면 아마도 일손이 부족할 때일 겁니다. 그 분야의 인력이 부족한 상태에서는 기술이 효력을 발휘하죠. 만두를 만들 수 있는 사람 수가 수요에 비해 확연히 부족해진다면 만두를 만드는 기술은 엄청난 가치가 있는 것처럼 보이겠죠. 오늘날의 프로그래밍처럼 '시대가 요구하는 기술'은 반드시 존재하게 마련입니다."
 

야채

한창 프로그래밍 열풍이 불었던 때가 기억납니다. 개발자 인력난 때문에 너나 할거없이 코딩을 배웠고 온라인 강의 사이트에는 프로그래밍 관련 강의가 넘쳐났죠. 그런데 요즘 시대가 요구하는 기술이 바뀌고 있는 것 같습니다. AI 기술은 하루가 다르게 세상을 바꿔가고 있어요. 책이 쓰인지 3년도 채 안됐는데 이제는 대규모 플랫폼 기업도 개발자를 해고하는게 현실이 될만큼요.  2023년 한 해동안 실리콘밸리에서도 26만명이 해고 통보를 받았습니다. 개발자들도 이미 생성형 AI를 활용해서 코딩을 하고 있고요. API 개발자는 작년 기준으로 60%가 생성AI를 사용한다고 하네요. 앞으로 '시대가 요구하는 기술'은 AI를 포함한 기반 기술들이 될 것 같습니다. 클라우드, AI칩, AI반도체 등등이요.
 
 

구스노키

"그런데 인생은 깁니다. 만두 수요가 늘어나면 만두 만드는 기술을 지닌 사람들도 그만큼 많아질 거고, 그중 더 맛있는 만두가 더 잘 팔리겠죠. 그러면 평균점에 돈을 지불하는 사람은 없어지고 말겁니다."
 

야채

코딩을 좀더 얘기해보자면, 사실 AI는 이미 2년 전부터 인간 평균 능력을 따라잡았습니다. 챗GPT가 코딩을 더 잘할수록, 개발자들의 사용이 만연해질수록 기술이 부족한 개발자들은 그만큼 입지가 좁아지겠죠. 번쩍 떠오른 산업에서 혜택을 보고자 참여하면 결국 오래 갈 수가 없다는 겁니다. 말씀하신대로 '돈을 지불할만한 사람'이 되기 어려우니까요. 제 경우엔, 글쎄요. 알면서도 만두를 월등히 맛있게 만들 자신은 없네요. 그래서 저는 차라리 만두집을 색 다르게 꾸미는 쪽을 선택하려고 합니다. 맛이 월등한 만두집은 못될지라도 방문할 때마다 즐거움을 주는 만두집이 되고 싶어요.
 
 

 

같은 것을 다르게 보고,
보이지 않는 것을 본다.

성공한 사람은 다른사람이 알지 못하는 것을 알고 있다든가, 남이 하지 못하는 것을 할 수 있었기에 돈을 벌 수 있었던 것이다. 이것이 전략의 출발점이이다. 그럴 수 있었던 이유는 자신만이 갖고 있는 스토리를 가지고 있었기에 개별 요소가 독자적인 의미를 갖기 시작한 것이다.

 
 

구스노키

"예컨대 무턱대고 인공지능을 접목시킨다고 해서 모든 게 해결되지는 않습니다. 먼저 시간적 깊이를 고려한 스토리가 있어야 합니다. 그 스토리 가운데 특정 부분에 인공지능을 넣으면 다른 요소와 이어져 비용이 낮아지거나 이익이 생기듯이, 전체적인 배경 속에서 비로소 인공지능의 효과가 나오는 겁니다. 인공지능이라는 요소 자체에 의미가 있는 게 아니라 어떤 맥락 속에 놓이느냐가 중요합니다. 가치를 창출하는 것은 독자적인 스토리거든요."
 

야채

어디선가 들었는데요. 돈을 벌기 위해 사업하는 사람도 성공할 수 있지만 진짜 진짜 성공하는 사람은 돈이 아니라 비전을 위해 일하는 사람이라고요. 쿠팡은 2010년 창업 후 줄곧 적자였습니다. 그럼에도 쿠팡 창업자의 의지는 굳건했어요. “100년 기업을 만들기 위해 ‘계획된 적자’를 감수하겠다. 물류에 과감히 투자해 매년 2만 명 이상 고용하겠다.” 그 이후에도 조 단위의 영업 손실을 겪었지만 시간이 지날수록 점점 줄여가더니 2023년 4분기에는 드디어 흑자 전환에 성공했습니다. 단순히 돈을 벌고 싶어서 했더라면 그 오랜 적자의 시간들을 견딜 수 있었을까요? 자기 스토리에 대한 믿음이 있었기 때문에 이런 역전 드라마가 가능했다고 생각합니다. 이렇게 사고하는 방법을 인사이드 아웃 사고방식이라고 해요. 
 
 
 

 

자신만의 논리와 스토리로
무장한 인사이드 아웃

 

Outside - In:

  • 인터넷을 비롯한 IT 기술의 발전은 인간의 사고를 아웃사이드 인의 방향으로 유도하는 면이 있다.
  • 적정한 해답이 어딘가에 있을 테니 전체적으로 폭넓게 외부에 있는 정보를 조사하고 거기서 좋은 것을 찾아내 문제를 해결하려고 한다. 
  • 미래에  어떻게 될 것인지를 무척이나 알고 싶어한다. 
  • 어떻게 될지 알아낸 뒤에 수많은 선택지 가운데서 옳은 해답을 고르려고 한다. 
  • "미국에서 검색엔진이란 걸 시작했다고 하는군. 우리도 만들기로 결정했으니 어서 수행하게" 라는 상사의 지시에 모두 성실하게 관련 자료를 조사한다.
"아무튼 우린 안돼!"
"이젠 한물간 콘텐츠야"
"고령화 현상 때문에"
"정부 규제가 너무 심해서"
  • 숲만 보고 나무를 보지 않는다. 그래서 모든 일을 환경과 상황 탓으로 돌린다. 
  • 외부 정보에서 답을 찾는다.
  • 업무 지시를 성실히 따른다.
  • 계획이 완성되어야 실행한다.

 

Inside - Out:

  • "세상을 완전히 뒤바꿔주겠어!" 하는 열정으로 호시탐탐 기회를 노리고 있다.
  • 완전한 미래 예측은 할 수 없음을 인정한다. 
  • 정보는 불완전해도 우선 자기 나름대로의 논리와 스토리가 있고, 나름의 행복한 결론이 있다.
"모르면 나중에 배워서 활용하면 돼"
  • 자신의 논리에서 답을 찾는다. 
  • 자신이 세운 목표를 따른다.
  • 우선 실행하고 계획을 수정한다.

 
 

# 넷플릭스

넷플릭스는 초창기에 DVD 대여 사업으로 10여년 유지했다. 고객이 인터넷으로 주문한 DVD를 봉투에 넣어 우송하고, 다 본 DVD는 다시 봉투에 넣어 돌려받는 시스템이었다.  넷플릭스의 가치는 '고객이 여러 가지 제약에 구애받지 않고 보고 싶은 방송 프로그램을 보고 싶을 때 보고 싶은 장소에서 볼 수 있게 한다.'는 단순한 컨셉이다. 그러나 블록버스터라는 골리앗은 막강했다. 한때는 넷플릭스를 궁지로 몰아넣는 상황까지 가기도 했다. 블록버스터 대여점에 가면 신작이나 화제작이 잔뜩 진열되어 있다. 그런데 신작은 금방 동나고, 고객은 불편이 발생한다. 이를 해소하려면 신작 재고를 늘려야 하는데 입고 가격을 감당할 수 없었고, 신작의 인기가 끝나면 금세 불량 재고로 쌓이게 된다. 따라서 한정된 자금과 재고로 고객을 만족시키기 위해서는 구작을 대여하도록 유도해 대여 작품을 평준화하는 방법을 사용해야 했다. 그러려면 고객이 어떤 영화를 어떤 순서로 보고 어느 정도 기간 안에 반납하며 다음에 무엇을 빌렸는지 상세히 조사하면 취향과 패턴을 파악할 수 있다. 이렇게 메일로 고객을 유도하면 구작 대여율을 높일 수 있다. 이렇게 데이터 마케팅을 2004년부터 해온 넷플릭스는 연간 수익이 꾸준히 상승했으며, 블록버스터는 기존 방식을 고수하다가 2013년에 도산했다.
 
 

# 데이터보다 인간을 신뢰한 스티브 잡스와 레고

일을 잘하는 사람은 전반적으로 인간에 대한 이해가 높다. 대표적으로 스티브 잡스는 시장조사에 매우 부정적이었다. 많은 사람들에게 일일이 물어보지 않아도 어떤 상품이 잘 팔릴지 직감적으로 파악할 수 있을만큼 인간을 이해하고 있기 때문이다. 레고 산업은 어린이에 대한 정보, 즉 데이터를 많이 모았다. 그 결과 요즘 아이들은 예전에 비해 주의가 산만하다는 통계 정보를 얻었다. 그래서 더이상 레고블록 같은 장난감은 더 이상 팔리지 않을 거라 체념하고, 새롭게 캐릭터 비즈니스를 모색해 변화를 시도했지만 실적은 더 악화되었다. 결국 '아이들은 왜 노는 걸까?' 하고 놀이의 본질을 다시 생각했을 때 레고가 부활했다. 인터넷 게임의 기세는 여전히 흔들리지 않았다. 이러한 현상만 보았다면 레고는 영원히 부활하지 못했을 것이다. 단순히 집계 수준의 평균치, 현상과 경향만 좇다 보면 피상적인 결론을 얻게 된다. 하지만 아이 한 명 한 명을 찬찬히 관찰하면 레고에 열중하고 있는 아이들을 볼 수 있다. 데이터 지상주의의 함정이다. 데이터는 조사 설계자가 검증하고자 하는 일면밖에 보여주지 못하므로 확증 편향을 더욱 강하게 만드는 경향이 있다. 인간에 대한 통찰이 무엇인가? 한 사람의 내면에 존재하는 상당히 복잡한 메커니즘에 대한 이해다. 그러니 데이터를 집계해서 평균치나 경향으로 상관관계를 파악하는 것은 맞지 않을 수밖에 없다.
 
 
 

야채

결국 중요한 건 인간에 대한 관찰, 나아가 통찰입니다. 데이터로는 설명되지 않는 근본적 원인을 통찰하는 힘이죠. 그리고 관찰을 통해 얻게 된 인사이트를 자기만의 스토리로 만들어가는 과정이 필요합니다. 스토리에 사용되는 요소들은 가능한 객관적이고 논리적일수록 좋아요. 하지만 '해결할 수 있을 것 같은 문제'는 이미 똑똑한 누군가가 해결 중일 가능성이 큽니다. 오히려 '절대 해결 못할 것 같은 난제'를 두고 내 스토리를 만들어가는게 더 빠를 거예요. 어떻게 풀어야할지 지금 당장은 몰라도 괜찮아요. AI가 올것이다 말은 많았지만, 이렇게까지 세상이 변할 줄은 아무도 몰랐잖아요? 해결의 실마리는 언제 어떻게 올지 모릅니다. 일단 시나리오를 준비하기로 해요!
 
 

[Python] 데이터 시각화

0ㅑ채
|2024. 2. 14. 16:24

1. 데이터 시각화 - matplotlib

1) 앤스콤 데이터

- 이 데이터는 4개의 데이터 그룹으로 구성

- 앤스콤 데이터 가져오기

#앤스콤 데이터 가져오기
import seaborn as sns
anscombe = sns.load_dataset("anscombe")
print(anscombe.head())
print(anscombe['dataset'].unique())
  dataset     x     y
0       I     10.0  8.04
1       I      8.0  6.95
2       I     13.0  7.58
3       I      9.0  8.81
4       I     11.0  8.33

['I' 'II' 'III' 'IV']

 

# 데이터셋 분리

#dataset 별로 분리
dataset_1 = anscombe[anscombe['dataset'] == 'I']
dataset_2 = anscombe[anscombe['dataset'] == 'II']
dataset_3 = anscombe[anscombe['dataset'] == 'III']
dataset_4 = anscombe[anscombe['dataset'] == 'IV']

print(dataset_1)

 

   dataset     x      y
0        I  10.0   8.04
1        I   8.0   6.95
2        I  13.0   7.58
3        I   9.0   8.81
4        I  11.0   8.33
5        I  14.0   9.96
6        I   6.0   7.24
7        I   4.0   4.26
8        I  12.0  10.84
9        I   7.0   4.82
10       I   5.0   5.68
  • 데이터셋 네개의 평균, 표준편차를 구해보면 모두 같다.
  • 상관계수도 큰 차이가 없다.
  • 그렇다고 해서 데이터들의 특성이 비슷할 것이라 판단하면 안됨!

 

# 앤스콤 데이터 각 세트 시각화

## 데이터 시각화
import matplotlib.pyplot as plt
fig = plt.figure()

axes1 = fig.add_subplot(2, 2, 1)
axes2 = fig.add_subplot(2, 2, 2)
axes3 = fig.add_subplot(2, 2, 3)
axes4 = fig.add_subplot(2, 2, 4)

axes1.plot(dataset_1['x'], dataset_1['y'], 'o')
axes2.plot(dataset_2['x'], dataset_2['y'], 'o')
axes3.plot(dataset_3['x'], dataset_3['y'], 'o')
axes4.plot(dataset_4['x'], dataset_4['y'], 'o')

axes1.set_title("dataset_1")
axes2.set_title("dataset_2")
axes3.set_title("dataset_3")
axes4.set_title("dataset_4")

  • axes4는 상관계수가 전혀 보이지 않는데
  • 이상치 하나가 평균이나 상관계수에 영향을 많이 준 것이다.

 

2) 자주 사용되는 시각화 라이브러리

- matplotlib: 가장 기본이 되는 라이브러리

- seaborn

- plotnine

- polt.ly

- pyecharts

- folium: 지도 시각화

 

 

3) matplotlib

- 시각화(그래프, 이미지)에 가장 많이 이용하는 라이브러리

- 파이썬의 대다수 시각화 기능

- 설치 필요

* 아나콘다는 내장

 

# 엑셀 파일 읽기

df = pd.read_excel('./data/시도_별_이동자수.xlsx', header=0)
df.head()

 

# 엑셀에서 셀 병합이 있으면 천번째를 제외하고는 NaN으로 처리

df = df.fillna(method="ffill")
df.head()

 

# 조건에 맞는 데이터 필터링

#전출지별 : 서울특별시
#전입지별 : 서욱특별시가 아닌 데이터
mask = (df['전출지별'] == '서울특별시') & (df['전입지별'] != '서울특별시')
df_seoul = df[mask]
df_seoul

 

# 컬럼 삭제

df_seoul = df_seoul.drop(['전출지별'], axis=1, inplace=True)

 

# 컬럼 이름변경

df_seoul.rename({'전입지별':'전입지'}, axis=1, inplace=True)

 

# 인덱스 설정

df_seoul.set_index('전입지', inplace=True)

 

 

4) 라인그래프 그리기

# 인덱스가 전라남도인 데이터 추출
sr_one = df_seoul.loc['전라남도']
sr_one

import matplotlib.pyplot as plt
plt.plot(sr_one.index, sr_one.values)
plt.show()
1970    10513
1971    16755
1972    20157
1973    22160

....

 

- matplotlib을 사용했을 때의 문제점

  • 한글이나 음수는 제대로 출력되지 않음

 

한글 처리

import matplotlib.pyplot as plt
#그래프 크기 설정 - 단위는 inch
plt.figure(figsize=(14, 5))
#x축 눈금 라벨 회전
plt.xticks(size=10, rotation='vertical')
plt.title('seoul->전라남도', size=20)
plt.plot(sr_one.index, sr_one.values)
plt.show()

 

from matplotlib import font_manager, rc
import platform

font_name = font_manager.FontProperties(fname="c:/Windows/Fonts/malgun.ttf").get_name()
rc('font', family=font_name)

 

#라인 그래프 출력
plt.plot(sr_one.index, sr_one.values, marker='o', markersize=5)

#축 제목
plt.xlabel('기간', size=20)
plt.ylabel('이동 인구수', size=20)

#범례
plt.legend(labels=['서울 -> 전라남도'], loc='best', fontsize=15)

plt.show()

5) 막대그래프 그리기

#그래프 크기 설정 - 단위는 inch
plt.figure(figsize=(14, 5))

# 제목 설정
plt.title('서울->전라남도', size=20)

# x축 눈금 라벨 회전
plt.xticks(size=10, rotation='vertical')

#막대 그래프
plt.bar(sr_one.index, sr_one, width =1.0)

plt.show()

 

6) 빈도수 그래프 (히스토그램)

df = pd.read_csv('./data/lovefruits.csv', encoding='cp949')
df
  • 웹사이트는 euk-kr
  • 파일은 cp949
data = df['선호과일'].value_counts(sort=False)
data
선호과일
사과      4
바나나     3
포도      5
복숭아    11
체리      8
Name: count, dtype: int64

 

막대그래프로 그리기 - 직접 빈도수 구해야 함

data = df['선호과일'].value_counts(sort=False)
plt.bar(range(0, len(data), 1), data)
plt.xticks(range(0, len(data), 1), data.index)

plt.show()

히스토그램 - 알아서 빈도수 구해줌

plt.hist(df['선호과일'])
plt.show()

 

 

7) 산포도(scatter)

- 데이터 분포나 상관관계를 파악하기 위한 그래프

- x와 y 옵션에 데이터를 설정

- s 옵션: 사이즈를 설정해서 크기로 다른데이터를 반영하는 것이 가능

mpg = pd.read_csv('./data/auto-mpg.csv', header=None)
mpg.columns = ['mpg', 'cylinders', 'displacement', 'horsepower', 'weight', 'acceleration', 'model year', 'origin', 'name']
plt.scatter(x=mpg['weight'], y=mpg['mpg'])
plt.show()

size = mpg['cylinders'] / mpg['cylinders'].max() * 200
plt.scatter(x=mpg['weight'], y=mpg['mpg'], s=size, c='coral', alpha=0.5)
plt.show()

 

 

8) pie 

- 기여도를 나타냄

 

9) box plot

- 데이터의 범위를 표현

 

10) fill_between

- 데이터 크기를 영역으로 나타내는 그래프 

 

 

 

2. Seaborn

- matplotlob 기반으로 다양한 색상 테마와 통계용 차트의 기능을 추가한 패키지

- numpy와 pandas의 자료구조 지원

- 샘플데이터 

  • 내장되어 있음
  • load_dataset 메소드로 호출, DataFrame으로 리턴
#회귀에서 사용하는 데이터 셋 가져오기
import seaborn as sns
tips = sns.load_dataset("tips")
print(tips.head())

 

1) 산점도와 회귀식을 출력

  • regplot(), implot() 이용
  • 산점도와 회귀선 같이 출력
  • 회귀선 출력하지 않으려면 fit_reg 옵션 설정을 수정
  • data에 데이터프레임을 설정하고 x와 y에 컬럼 이름 설정
plt.figure(figsize=(8,6)) #캔버스 크기 설정
sns.regplot(x="total_bill", y="tip", data=tips)
plt.show()

 

# 회귀선 두개 옵션

plt.figure(figsize=(8,6)) #캔버스 크기 설정
sns.lmplot(x="total_bill", y="tip", hue = 'smoker', data=tips)
plt.show()

 

2) heatmap

- 기간별 추세를 알아보고자 할 때 사용

- 가로와 세로 모두 기간을 설정하고 색상을 이용해서 크기 설정

- 라인 그래프는 일반적으로 가로 방향에만 기간 설정

- 히트맵을 출력할 때는 범례나 레이블을 출력해줘야 값의 변화를 파악하기 쉽다. 

flights = sns.load_dataset("flights")
print(flights)
     year month  passengers
0    1949   Jan         112
1    1949   Feb         118
2    1949   Mar         132
3    1949   Apr         129
4    1949   May         121
..    ...   ...         ...
print(flights.pivot(columns=("month", "year", "passengers")))
Empty DataFrame
Columns: []
Index: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, ...]

[144 rows x 0 columns]
data = flights.pivot(index="month", columns="year", values="passengers")
print(data)
year   1949  1950  1951  1952  1953  1954  1955  1956  1957  1958  1959  1960
month                                                                        
Jan     112   115   145   171   196   204   242   284   315   340   360   417
Feb     118   126   150   180   196   188   233   277   301   318   342   391
Mar     132   141   178   193   236   235   267   317   356   362   406   419
Apr     129   135   163   181   235   227   269   313   348   348   396   461
May     121   125   172   183   229   234   270   318   355   363   420   472
Jun     135   149   178   218   243   264   315   374   422   435   472   535
Jul     148   170   199   230   264   302   364   413   465   491   548   622
Aug     148   170   199   242   272   293   347   405   467   505   559   606
Sep     136   158   184   209   237   259   312   355   404   404   463   508
Oct     119   133   162   191   211   229   274   306   347   359   407   461
Nov     104   114   146   172   180   203   237   271   305   310   362   390
Dec     118   140   166   194   201   229   278   306   336   337   405   432
sns.heatmap(data, annot=True, fmt='d')
기본  annot = True fmt = 'd'

 

 

3) 데이터 분포를 출력해주는 다양한 그래프

- boxplot: 4분위수와 IQR * 1.5배 되는 데이터의 위치 및 그 이외의 데이터 분포 확인

* IQR: (3/4-1/4)

 

- violinplot: 4분위 수 대신에 두께를 이용해서 데이터의 밀집 형태 판단할 수 있음

 

- stripplot: 두께가 아니라 점으로 데이터의 밀집 형태 표시 (점이 겹침)

 

- swarmplot: 두께가 아니라 점으로 데이터의 밀집 형태 표시 (점이 겹치지 않음)

 

- box plot과 swarmplot을 같이 추력하면 데이터의 밀집 형태 확인 가능

 

- pairplot: 데이터프레임의 컬럼이 숫자로만 된 경우 2개씩 조합해서 모든 경우의 조합의 산포도 출력

 

# boxplot

tips = sns.load_dataset('tips')
sns.boxplot(x='day', y='total_bill', data=tips)
plt.show()

 

# violinplot

sns.violinplot(x='day', y='total_bill', data=tips)
plt.show()

  • 데이터의 밀집 형태를 두께감으로 표시

# stripplot

sns.stripplot(x='day', y='total_bill', hue='smoker', data=tips)
plt.show()

# swarmplot

sns.swarmplot(x='day', y='total_bill', data=tips)
plt.show()

# box plot과 swarmplot을 같이

sns.boxplot(x='day', y='total_bill', data=tips)
sns.swarmplot(x='day', y='total_bill', data=tips)
plt.show()

# pariplot

sns.pairplot(tips)
plt.show()

  • tip과 total_bill만 상관관계
sns.pairplot(tips[['total_bill', 'tip', 'size']])
plt.show()
  • 숫자 데이터만 넣어주는게 바람직

 

 

3. Plotnine

- R의 ggplot2에 기반해서 그래프를 그려주는 라이브러리

- 기본 패키지가 아니라서 설치 필요

- 도큐먼트: https://plotnine.readthedocs.io/en/stable 

- 파이썬을 매개변수를 설정할 때 쉼표(,)로 구분하는데, R은 +로 결합 가능

import plotnine
df = pd.DataFrame({
    'letter':["Alpha", 'Beta', 'Delta', 'Gamma'] * 2,
    'pos':[1, 2, 3, 4] * 2,
    'num_of_letters': [5, 4, 5, 5] * 2
})

(plotnine.ggplot(df)
+ plotnine.geom_col(plotnine.aes(x='letter', y='pos', fill='letter')) 
+ plotnine.geom_line(plotnine.aes(x='letter', y='num_of_letters', color='letter'), size=1) 
+ plotnine.scale_color_hue(I=0.45)
+ plotnine.ggtitle('Greek Letter Analysis')
)

 

 

4. plotly

- 인터럭티브한 그래프를 그려주는 라이브러리

- https://plotly.com/python 

- D3.js 라이브러리 활용

  • 자바스크립트로 차트를 html에 표현하는 라이브러리
  • 웹상에서 동적인 차트 가능

- 설치: chart_studio 라이브러리도 함께 설치

 

 

5. folium

- 지도를 그려주는 라이브러리

- 자바스크립트 기반으로 인터럭티브한 그래프

- 설치 필요

- Map() 함수를 이용해서 지도 객체를 생성

  • locations 옵션에 중앙점의 위도와 경도 설정하고 zoom_start 옵션에 초기 확대 축소 배율 설정

- Chrome 브라우저를 사용하는 Jupyter Notebook에서는 바로 출력이 가능, 그 이외의 경우에는 html로 저장해서 확인

- 단계 구분도 생성 가능

import folium

m = folium.Map(location = [37.572656, 126.973300], zoom_start=15)
m

 

지도 위에 마커 표시

folium.Marker(location=[위도, 경도], popup='보여지는 문자열', icon=이미지모양).add_to(맵객체)
folium.Marker(location=[37.572656, 126.973300], popup='여기예요', icon=folium.Icon(color='red')).add_to(m)
m

 

 

 

 

 

 

 

'Python' 카테고리의 다른 글

[Python] 데이터 스케일링  (1) 2024.02.16
[Python] 데이터 전처리  (0) 2024.02.15
[Python] 데이터 탐색  (0) 2024.02.14
[Python] MySQL 데이터로 DataFrame 만들기  (0) 2024.02.14
[Python] 크롤링 - Selenium  (0) 2024.02.14

[Python] 데이터 탐색

0ㅑ채
|2024. 2. 14. 16:24

1. 데이터프레임에서의 데이터 선택

1) 열 선택

- 데이터프레임['컬럼이름'] 또는 데이터프레임.컬럼이름 

  • 데이터프레임.컬럼이름 으로 접근할 때는 컬럼이름이 반드시 문자열이어야 함

- 하나의 컬럼이름을 이용해서 접근하면 Series로 리턴

 

2) 행 선택

- loc[인덱스이름] 

- iloc[정수형 위치 인덱스] 

- Series로 리턴

 

3) 셀 선택

- [컬럼이름][인덱스이름]

- loc[인덱스이름, 컬럼이름]

- iloc[행 위치 인덱스, 열 위치 인덱스]

 

4) 다중 선택

- list를 이용해서 선택

  • DataFrame이 리턴
#item.csv 파일을 읽어서 DataFrame 만들기
#csv를 읽을 때 확인할 3가지
#한글 포함 여부 - 인코딩
# 구분자는 , 인지
#첫번째 줄이 컬럼이름인지 아니면 데이터인지
#컬럼 중에 primary key의 역할을 할 수 있는게 있는지

item = pd.read_csv('./data/item.csv')
print(item.head())
item.info()
   code  manufacture            name  price
0     1        korea           apple   1500
1     2        korea      watermelon  15000
2     3        korea  oriental melon   1000
3     4  philippines          banana    500
4     5        korea           lemon   1500

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 6 entries, 0 to 5
Data columns (total 4 columns):
 #   Column       Non-Null Count  Dtype 
---  ------       --------------  ----- 
 0   code         6 non-null      int64 
 1   manufacture  6 non-null      object
 2   name         6 non-null      object
 3   price        6 non-null      int64 
dtypes: int64(2), object(2)
memory usage: 324.0+ bytes

 

#현재 사용 중인 컬럼을 인덱스로 활용

item.index = item['code']
item.index = ['사과', '수박', '참외', '바나나', '레몬', '망고']
print(item)
     code  manufacture            name  price
사과      1        korea           apple   1500
수박      2        korea      watermelon  15000
참외      3        korea  oriental melon   1000
바나나     4  philippines          banana    500
레몬      5        korea           lemon   1500
망고      6        korea           mango    700

 

#열 하나 선택

print(item['name']) 
print(item.price)

print(item[['name']])

#type
print(type(item['name']))
print(type(item[['name']]))
사과              apple
수박         watermelon
참외     oriental melon
바나나            banana
레몬              lemon
망고              mango
Name: name, dtype: object
사과      1500
수박     15000
참외      1000
바나나      500
레몬      1500
망고       700
Name: price, dtype: int64
               name
사과            apple
수박       watermelon
참외   oriental melon
바나나          banana
레몬            lemon
망고            mango
<class 'pandas.core.series.Series'>
<class 'pandas.core.frame.DataFrame'>

 

#여러 열 선택

print(item[['name', 'price']])
               name  price
사과            apple   1500
수박       watermelon  15000
참외   oriental melon   1000
바나나          banana    500
레몬            lemon   1500
망고            mango    700

 

#행선택

print(item.iloc[0]) #0번째 행
print(item.loc['apple']) #사과라는 인덱스를 가진 행
code               1
manufacture    korea
name           apple
price           1500
Name: 사과, dtype: object

 

#셀선택

print(item['name'][2]) #name 컬럼의 3번째 데이터
oriental melon

 

 

5) 범위를 이용한 행 인덱싱

[시작위치 : 종료위치 : 간격] 

print(item.iloc[1:4]) #위치 인덱스에서는 마지막 위치가 포함되지 않음
print(item.loc["수박":"바나나"]) #이름 인덱스 에서는 마지막 위치가 포함됨
     code  manufacture            name  price
수박      2        korea      watermelon  15000
참외      3        korea  oriental melon   1000
바나나     4  philippines          banana    500
     code  manufacture            name  price
수박      2        korea      watermelon  15000
참외      3        korea  oriental melon   1000
바나나     4  philippines          banana    500

 

6) Boolean 인덱싱

- bool 타입의 Seriest를 대입하면 True 인 행 만 선택

- Series객체 비교연산자 값 이용하면 bool 타입의 Series를 리턴

  • item['price'] > 3000: price 가 3000 이하이면 False 3000 초과면 True를 리턴

- & 와 | 를 이용한 결합도 가능

#price가 1500 미만인 행만 추출
print(item[item['price'] < 1500])

#price가 1000 ~ 1500 인 데이터만 추출
print(item[(item['price']>=1000) & (item['price'] <= 1500)])
     code  manufacture            name  price
참외      3        korea  oriental melon   1000
바나나     4  philippines          banana    500
망고      6        korea           mango    700
    code manufacture            name  price
사과     1       korea           apple   1500
참외     3       korea  oriental melon   1000
레몬     5       korea           lemon   1500

 

- isin([데이터 나열]): 데이터 안에 속하면 True 그렇지 않으면 False를 리턴

#price 가 1000 또는 500 인 데이터 추출
print(item[item['price'].isin([1000, 500])])
     code  manufacture            name  price
참외      3        korea  oriental melon   1000
바나나     4  philippines          banana    500

 

 

 

2. 내용 확인

1) head 와 tail

- DataFrame의 데이터 중에서 앞 이나 뒤에서 몇 개의 데이터를 확인하고자 할 때 사용

 

2) shape

- 행과 열의 개수를 tuple 형식으로 리턴

 

3) info()

- DataFrame의 기본 정보를 리턴하는 함수

  • 데이터 유형
  • 행 인덱스의 구성
  • 열 이름
  • 각 열의 자료형 과 데이터 개수
  • 메모리 사용량

 

4) dtypes

- 각 열의 자료형 정보를 리턴

 

5) count()

- 데이터의 개수

 

6) value_counts()

- Series에서만 사용이 가능한데 고유한 값의 종류 와 개수 정보

 

7) describe()

- 기술 통계 정보를 출력

- 옵션이 없으면 숫자 데이터의 평균, 표준 편차, 최대값, 최소값, 중간값

- include='all' 옵션으로 추가하면 숫자 데이터가 아닌 열의 unique, top, freq 를 출력

 

8) auto-mpg.csv 파일의 데이터 확인

- 자동차 연비 와 관련된 데이터 셋으로 회귀에 사용

- 컬럼

  • mpg: 연비
  • cylinders: 실린더 개수
  • displacement: 배기량
  • horsepower: 출력
  • weight: 중량
  • acceleration: 가속 능력
  • model_year: 출시 년도
  • origin: 제조국
  • name: 모델명

- 데이터 확인

#헤더가 없어서 컬럼 이름을 직접 설정
df = pd.read_csv('./data/auto-mpg.csv', header=None)
df.columns = ['mpg', 'cylinders', 'displacement', 'horsepower', 'weight', 'acceleration', 'model year', 'origin', 'name']
#처음 5개의 데이터만 확인
print(df.head())
    mpg  cylinders  displacement horsepower  weight  acceleration  model year  \
0  18.0          8         307.0      130.0  3504.0          12.0          70   
1  15.0          8         350.0      165.0  3693.0          11.5          70   
2  18.0          8         318.0      150.0  3436.0          11.0          70   
3  16.0          8         304.0      150.0  3433.0          12.0          70   
4  17.0          8         302.0      140.0  3449.0          10.5          70   

   origin                       name  
0       1  chevrolet chevelle malibu  
1       1          buick skylark 320  
2       1         plymouth satellite  
3       1              amc rebel sst  
4       1                ford torino  
#행 과 열의 수 확인
print(df.shape)
#자료형 확인
print(df.dtypes)
#데이터 개수
print(df.count())
(398, 9)

mpg             float64
cylinders         int64
displacement    float64
horsepower       object
weight          float64
acceleration    float64
model year        int64
origin            int64
name             object
dtype: object

mpg             398
cylinders       398
displacement    398
horsepower      398
weight          398
acceleration    398
model year      398
origin          398
name            398
dtype: int64
#앞의 3가지 정보를 전부 확인 가능하고 null(None)도 확인 가능
df.info()

#기술 통계 확인 - 숫자 데이터의 기술 통계
print(df.describe())

#기술 통계 확인 - 모든 데이터의 기술 통계
print(df.describe(include='all'))
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 398 entries, 0 to 397
Data columns (total 9 columns):
 #   Column        Non-Null Count  Dtype  
---  ------        --------------  -----  
 0   mpg           398 non-null    float64
 1   cylinders     398 non-null    int64  
 2   displacement  398 non-null    float64
 3   horsepower    398 non-null    object 
 4   weight        398 non-null    float64
 5   acceleration  398 non-null    float64
 6   model year    398 non-null    int64  
 7   origin        398 non-null    int64  
 8   name          398 non-null    object 
dtypes: float64(4), int64(3), object(2)
memory usage: 28.1+ KB

              mpg   cylinders  displacement       weight  acceleration  \
count  398.000000  398.000000    398.000000   398.000000    398.000000   
mean    23.514573    5.454774    193.425879  2970.424623     15.568090   
std      7.815984    1.701004    104.269838   846.841774      2.757689   
min      9.000000    3.000000     68.000000  1613.000000      8.000000   
25%     17.500000    4.000000    104.250000  2223.750000     13.825000   
50%     23.000000    4.000000    148.500000  2803.500000     15.500000   
75%     29.000000    8.000000    262.000000  3608.000000     17.175000   
max     46.600000    8.000000    455.000000  5140.000000     24.800000   

       model year      origin  
count  398.000000  398.000000  
mean    76.010050    1.572864  
std      3.697627    0.802055  
min     70.000000    1.000000  
25%     73.000000    1.000000  
50%     76.000000    1.000000  
75%     79.000000    2.000000  
max     82.000000    3.000000  

               mpg   cylinders  displacement horsepower       weight  \
count   398.000000  398.000000    398.000000        398   398.000000   
unique         NaN         NaN           NaN         94          NaN   
top            NaN         NaN           NaN      150.0          NaN   
freq           NaN         NaN           NaN         22          NaN   
mean     23.514573    5.454774    193.425879        NaN  2970.424623   
std       7.815984    1.701004    104.269838        NaN   846.841774   
min       9.000000    3.000000     68.000000        NaN  1613.000000   
25%      17.500000    4.000000    104.250000        NaN  2223.750000   
50%      23.000000    4.000000    148.500000        NaN  2803.500000   
75%      29.000000    8.000000    262.000000        NaN  3608.000000   
max      46.600000    8.000000    455.000000        NaN  5140.000000   

        acceleration  model year      origin        name  
count     398.000000  398.000000  398.000000         398  
unique           NaN         NaN         NaN         305  
top              NaN         NaN         NaN  ford pinto  
freq             NaN         NaN         NaN           6  
mean       15.568090   76.010050    1.572864         NaN  
std         2.757689    3.697627    0.802055         NaN  
min         8.000000   70.000000    1.000000         NaN  
25%        13.825000   73.000000    1.000000         NaN  
50%        15.500000   76.000000    1.000000         NaN  
75%        17.175000   79.000000    2.000000         NaN  
max        24.800000   82.000000    3.000000         NaN

 

 

 

3. DataFrame 이름변경

1) rename()

- 인덱스나 컬럼의 이름을 변경하고자할 때 사용

- index: 딕셔너리 형태로 {기존 인덱스 : 새로운 인덱스, ...} 설정

  • index 변경은 메소드를 이용 X
  • list나 Series 형태로 설정 가능

- Columns: 딕셔너리 형태로 {기존 컬럼 이름 : 새로운 컬럼 이름, ...} 설정하면 컬럼 이름 변경

- inplace: 이 옵션의 기본값은 False 인데 False 가 설정되면 복제본을 만들어서 리턴하고 True를 설정하면 원본이 변경

- rename: 함수는 첫번째 매개변수로 변환 함수를 대입하고 두번째 옵션에 axis 에 index 나 columns를 설정해서 변환 함수를 이용해서 변경하는 것도 가능

 

# 컬럼 이름 변경

import pandas as pd
item = pd.read_csv('./data/item.csv')

item.rename(columns={"code":"코드", "manufacture":"원산지", "name":"이름", "price":"가격"})

 

 

  • 만약에 아래와 같이 한다면 아무 일도 일어나지 않는다.
names = {"code":"코드", "manufacture":"원산지", "name":"이름", "price":"가격"}
item.rename(columns=names)

#아무 일도 일어나지 않는다
#numpy나 pandas의 대다수 메소드는 원본을 변경하지 않고 수정해서 리턴
  • 원본을 수정할 때 inplace 옵션 사용
item.rename(columns=names, inplace=True)
#inplace 옵션이 있는지 확인하고 True로 해주면 원본을 수정

 

 

2) 인덱스의 재구성

- 인덱스

  • 행을 구별하기 위한 이름
  • 의미있는 이름을 인덱스로 설정
    • 관계형 데이터베이스에서 기본키처럼 데이터 식별이 가능한 값
  • 기본값은 0부터 일련번호
  • index: 데이터 프레임 생성하면서 인덱스 지정 
  • reindex: index를 재배치, 추가, 삭제 가능
  • set_index(열이름 or 열이름 나열): 컬럼을 인덱스로 사용하는 것이 가능, 컬럼 중에서는 제거
  • reset_index(): 기본 인덱스 제거, 0부터 시작하는 일련번호 다시 부여. 피봇 형태에서 많이 사용

 

index 옵션 활용

# item에서 '코드'를 가져와서 인덱스로 설정, 컬럼으로 존재
item.index = item.코드
print(item)
    코드          원산지              이름     가격
코드                                        
1    1        korea           apple   1500
2    2        korea      watermelon  15000
3    3        korea  oriental melon   1000
4    4  philippines          banana    500
5    5        korea           lemon   1500
6    6        korea           mango    700

 

set_index 옵션 활용

#컬럼에서 제거되고 index로 설정
item.set_index("코드")
print(item.set_index("코드"))
            원산지              이름     가격
코드                                    
1         korea           apple   1500
2         korea      watermelon  15000
3         korea  oriental melon   1000
4   philippines          banana    500
5         korea           lemon   1500
6         korea           mango    700

 

reset_index 옵션 활용

item = item.reset_index()
print(item)
   코드          원산지              이름     가격
0   1        korea           apple   1500
1   2        korea      watermelon  15000
2   3        korea  oriental melon   1000
3   4  philippines          banana    500
4   5        korea           lemon   1500
5   6        korea           mango    700

 

 

 

2. 데이터 삭제

1) drop 

- 행이나 열을 삭제

- 인덱스나 컬럼 이름을 하나 또는 list 형태로 대입

  • axis=0  : 행 제거
  • axis=1  : 열 제거

- inplace 옵션 존재  추천 X

#2행 삭제
print(item.drop([1], axis=0))

#code 열 삭제
print(item.drop(['code'], axis=1))

 

2) del

- 컬럼 제거 del DataFrame이름['컬럼이름']   추천 X

  • 다른 변수에 수정한 값을 넣으면, 원본을 보존할 수 있음

 

 

3. 데이터 수정 및 추가

- 컬럼 이름이나 인덱스는 유일무이

- DataFrame은 dict처럼 동작

DataFrame[컬럼이름] = 데이터
  • 데이터를 대입할 때 하나의 값이나 Vector 데이터 (list, ndarray, Series, dict) 사용

- 행 수정 및 추가

DataFrame.loc[인덱스이름] = 데이터
  • 인덱스 이름이 존재하지 않으면 추가, 인덱스 이름이 존재하면 수정!

# 하나의 값을 설정하면 모든 행 값이 동일한 값으로 대입

item['description'] = '과일'
print(item)
   code  manufacture            name  price description
0     1        korea           apple   1500          과일
1     2        korea      watermelon  15000          과일
2     3        korea  oriental melon   1000          과일
3     4  philippines          banana    500          과일
4     5        korea           lemon   1500          과일
5     6        korea           mango    700          과일

 

# dict를 이용하면 key와 index가 일치할 때 값을 대입

item['description'] = ['사과', '수박', '참외', '바나나', '레몬', '망고']
print(item)
   code  manufacture            name  price description
0     1        korea           apple   1500          사과
1     2        korea      watermelon  15000          수박
2     3        korea  oriental melon   1000          참외
3     4  philippines          banana    500          바나나
4     5        korea           lemon   1500         레몬
5     6        korea           mango    700          망고

 

# 컬럼 수정 - Series나 dict는 인덱스나 키 이름대로 대입

item['description'] = {0:'사과', 1:'수박', 2:'딸기', 5:'포도', 4:'바나나', 3:'망고'}
print(item)
   code  manufacture            name  price description
0     1        korea           apple   1500          사과
1     2        korea      watermelon  15000          수박
2     3        korea  oriental melon   1000          딸기
3     4  philippines          banana    500          망고
4     5        korea           lemon   1500         바나나
5     6        korea           mango    700          포도

 

# 행 추가

item.loc[6] = [7, '한국', '무화과', 3000, '무화과']
print(item)
   code  manufacture            name  price description
0     1        korea           apple   1500          사과
1     2        korea      watermelon  15000          수박
2     3        korea  oriental melon   1000          딸기
3     4  philippines          banana    500          망고
4     5        korea           lemon   1500         바나나
5     6        korea           mango    700          포도
6     7           한국             무화과   3000         무화과

 

# 특정 셀 수정

item.loc[6, 'name'] = "fig"
print(item)
   code  manufacture            name  price description
0     1        korea           apple   1500          사과
1     2        korea      watermelon  15000          수박
2     3        korea  oriental melon   1000          딸기
3     4  philippines          banana    500          망고
4     5        korea           lemon   1500         바나나
5     6        korea           mango    700          포도
6     7           한국             fig   3000         무화과

 

 

 

4. 연산

1) 전치 연산

- 행과 열을 전환하는 연산

- T 속성 이용

- transpose() 함수 이용

 

2) 산술 연산

- numpy와 동일한 방식으로 연산 수행 (브로드캐스트 연산)

- numpy는 위치 기반으로 연산을 수행하지만, Series나 DataFrame은 인덱스 기반으로 연산수행

- 한쪽에만 존재하는 경우 NaN으로 결과 설정

- 산술 연산자 사용 가능

- add, sub, div, mul 메소드 이용 가능. fill_value 옵션에 한쪽에만 존재하는 인덱스에 기본값 설정

 

item1 = {
    "1":{'price':1000}, 
    "2":{'price':2000}
}

item2 = {
    "1":{'price':1000}, 
    "3":{'price':3000}
}

df1 = pd.DataFrame(item1).T
df2 = pd.DataFrame(item2).T
   price
1   1000
2   2000

   price
1   1000
3   3000

 

# 브로드캐스트 연산

print(df1 + 200) #200을 df1의 개수만큼 복제해서 연산
   price
1   1200
2   2200

 

# 데이터프레임끼리 산술 연산

print(df1 + df2)
    price
1  2000.0
2     NaN
3     NaN
  • 존재하지 않는 인덱스 결과는 Nan(None과 조금 다름)

# 함수 사용 연산

print(df1.add(df2, fill_value=0))
    price
1  2000.0
2  2000.0
3  3000.0
  • 한쪽에만 존재하는 인덱스에 기본값 설정해서 연산 수행

# 행 단위 연산

print(df1.add(df2, axis=0))
    price
1  2000.0
2     NaN
3     NaN

 

 

3) 기본 통계 함수

- count, min, max, sum, mean, median, mode(최빈값)

- var(분산), std(표준 편차), kurt(첨도), skew(왜도), sem(평균의 표준 오차)

- argmin, argmax, dixmin, dixmax

- quantile(4분위수)

- describe(기술 통계 정보 요약)

- cumsum, cummin, cummax, cumprod: 누적합, 누적최소, 누적최대, 누적곱

- diff(산술적인 차이)

- pct_change(이전 데이터와의 백분율)

- unique(): Series에서만 사용 가능한데 동일한 값을 제외한 데이터의 배열 리턴하는 데 skipna 옵션을 이용해서 NaN 제거 가능

 

mpg = pd.read_csv("./data/auto-mpg.csv", header=None)
mpg.columns = ['mpg', 'cylinders', 'displacement', 'horsepower', 'weight', 
              'acceleration', 'model year', 'origin', 'name']
print(mpg.head())
    mpg  cylinders  displacement horsepower  weight  acceleration  model year  \
0  18.0          8         307.0      130.0  3504.0          12.0          70   
1  15.0          8         350.0      165.0  3693.0          11.5          70   
2  18.0          8         318.0      150.0  3436.0          11.0          70   
3  16.0          8         304.0      150.0  3433.0          12.0          70   
4  17.0          8         302.0      140.0  3449.0          10.5          70   

   origin                       name  
0       1  chevrolet chevelle malibu  
1       1          buick skylark 320  
2       1         plymouth satellite  
3       1              amc rebel sst  
4       1                ford torino  

 

# 기술 통계 함수

print(mpg[['mpg']].mean())

print(mpg[['mpg', 'weight']].mean())
mpg    23.514573
dtype: float64

mpg         23.514573
weight    2970.424623
dtype: float64

 

# describe()

print(mpg.describe())
              mpg   cylinders  displacement       weight  acceleration  \
count  398.000000  398.000000    398.000000   398.000000    398.000000   
mean    23.514573    5.454774    193.425879  2970.424623     15.568090   
std      7.815984    1.701004    104.269838   846.841774      2.757689   
min      9.000000    3.000000     68.000000  1613.000000      8.000000   
25%     17.500000    4.000000    104.250000  2223.750000     13.825000   
50%     23.000000    4.000000    148.500000  2803.500000     15.500000   
75%     29.000000    8.000000    262.000000  3608.000000     17.175000   
max     46.600000    8.000000    455.000000  5140.000000     24.800000   

       model year      origin  
count  398.000000  398.000000  
mean    76.010050    1.572864  
std      3.697627    0.802055  
min     70.000000    1.000000  
25%     73.000000    1.000000  
50%     76.000000    1.000000  
75%     79.000000    2.000000  
max     82.000000    3.000000  
  • 무의미한 컬럼(origin)의 기술통계가 같이 구해짐
mpg['origin'] = mpg['origin'].astype('str')
print(mpg.describe())
  • 형변환 해주기!

 

4) 상관관계 파악

- 상관관계: 2개의 데이터의 동일한 또는 반대되는 경향을 갖는 관계

  • 상관관계가 높다는 것은 동일한 경항 또는 완전히 반대되는 경향을 갖는 경우

- cov(): 공분산 

  • 거리의 제곱

- corr(): 상관계수

  • 공분산과 데이터의 스케일을 맞추지 않고 모든 값이 동일한 스케일을 갖도록 값을 수정
  • -1 ~ 1이 되도록 수정
  • 절대값 1에 가까워지면 상관관계가 높고, 0에 가까워지면 상관관계가 낮음
print(mpg[['mpg', 'cylinders', 'displacement']].corr())
                               mpg              cylinders         displacement
mpg                    1.000000        -0.775396          -0.804203
cylinders            -0.775396       1.000000            0.950721
displacement    -0.804203        0.950721           1.000000

 

 

5) 정렬

- 인덱스나 컬럼 이름에 따른 정렬

  • sort_index() 메소드 이용 
  • 기본은 인덱스가 기준
  • 오름차순이 기본. 내림차순을 할 때는 ascending = False 설정
  • axis=1 : 컬럼 이름을 기준으로 정렬 수행

- 컬럼의 값을 기준으로 정렬

  • sort_values(by = 열 이름 또는 열 이름의 list, ascending=bool 또는 bool의 list)

 

6) 순위

- rank 함수 이용

  • 기본 오름차순
  • ascending = False 내림차순
  • axis로 행 열 단위 설정

- 동일한 점수가 있는 경우 기본적으로 순위의 평균 리턴

  • method 옵션에 max, min, first 설정해서 동일한 점수 처리하는 것이 가능

- 순위는 컬럼 단위로 연산 수행

  • 컬럼의 개수가 2개면 순위도 2개 리턴

#내림차순

print(mpg.sort_values(by=['mpg'], ascending=[False]))
      mpg  cylinders  displacement horsepower  weight  acceleration  \....
322  46.6          4          86.0      65.00  2110.0          17.9   
329  44.6          4          91.0      67.00  1850.0          13.8   
325  44.3          4          90.0      48.00  2085.0          21.7   
394  44.0          4          97.0      52.00  2130.0          24.6   
326  43.4          4          90.0      48.00  2335.0          23.7   
..    ...        ...           ...        ...     ...           ...   

 

#오름차순

print(mpg.sort_values(by=['mpg', 'displacement'], ascending=[True, True]))
      mpg  cylinders  displacement horsepower  weight  acceleration  \
28    9.0          8         304.0      193.0  4732.0          18.5   
26   10.0          8         307.0      200.0  4376.0          15.0   
25   10.0          8         360.0      215.0  4615.0          14.0   
27   11.0          8         318.0      210.0  4382.0          13.5   
124  11.0          8         350.0      180.0  3664.0          11.0   
..    ...        ...           ...        ...     ...           ...   

 

#동일한 값은 순위의 평균

print(mpg.rank())
       mpg  cylinders  displacement  horsepower  weight  acceleration  \
0    116.0      347.0         324.0        74.0   290.0          36.5   
1     61.5      347.0         352.5       132.5   309.0          27.0   
2    116.0      347.0         334.0       111.5   284.0          15.0   
3     81.0      347.0         315.0       111.5   283.0          36.5   
4     96.0      347.0         306.0        87.0   287.0          11.0   
..     ...        ...           ...         ...     ...           ...   
  • .5는 같은 값이 2개라는 거

 

#동일한 값은 낮은 순위 부여

print(mpg.rank(method='min'))
       mpg  cylinders  displacement  horsepower  weight  acceleration  \
0    108.0      296.0         323.0        72.0   290.0          32.0   
1     54.0      296.0         344.0       131.0   309.0          24.0   
2    108.0      296.0         326.0       101.0   284.0          12.0   
3     75.0      296.0         312.0       101.0   283.0          32.0   
4     93.0      296.0         301.0        84.0   287.0          11.0   
..     ...        ...           ...         ...     ...           ...   

 

'Python' 카테고리의 다른 글

[Python] 데이터 전처리  (0) 2024.02.15
[Python] 데이터 시각화  (0) 2024.02.14
[Python] MySQL 데이터로 DataFrame 만들기  (0) 2024.02.14
[Python] 크롤링 - Selenium  (0) 2024.02.14
[Python] 크롤링 - 기사 스크래핑  (0) 2024.02.14