data analysis & visualization

python 얘기를 시작하기전에 우선 나는 python을 싫어했다.

 

나는 데이터 분석 업무를 맞고 있는 1년차 직장인이다. 

 

나는 대학교 2학년 때부터 AI 업무를 진행하였다. 그 때 접하게 된 프로그래밍 언어는 R이였다. 

 

이렇게 R언어는 내 분석업무에 주가 되주는 언어가 되었다. 

 

하지만 취업의 문 앞에서 python을 선택할 수 밖에 없었다.

 

취업 당시 R은 통계분석에서는 python보다 강력했으나, 웹 기반 서비스인 shiny는 Django나 Flask 대비 무거웠고,

 

딥러닝쪽에서 조차 힘을 쓰지 못하였었다.

 

내가 싫어했던 이유는 사실 프로그래밍언어가 어려워서가 아니였던 것 같다.

 

자유도 측면에서 python은 거부감이 많이 들었던 것 같다. 

 

파이썬은 개발을 고려한 프로그래밍이라 묵시적인 코딩 규칙들이 있었고,

나는 비전공자라 이러한 코딩 규칙은 전혀 무지하였다.

 

또한 여러가지 분석가가 몰라도 되는 애러 이슈들이 많이 있다. 

 

이외에도 많은 말도안되는 이슈들이 존재한다. 하지만 오늘은 한 블로그에 정리된 문제점을 소개하고자 한다.

 

github.com/pablogsal/python-horror-show

 

pablogsal/python-horror-show

Strange and odd python snippets explained. Contribute to pablogsal/python-horror-show development by creating an account on GitHub.

github.com

해당 블로그에서는 1. python의 정수 객체 처리 문제 2. 메모리 스왑 문제 3. 무한 반복 생성 4. Hashable Object 문제

 

5. Iterator 관련 문제 6. 논리 우선순위 문제 7. 객체 변환 문제 등이 있다. 

 

Python의 정수 객체 처리

 

python은 -5 ~ 256까지의 정수 객체 생성에서는 기존 생성된 메모리를 공유한다.

 

그래서 객체를 생성할 때 매번 다른 메모리에 재할당 되는데 -5~256은 같은 결과가 나타난다.

 

 

 

메모리 스왑

 

python의 메모리 스왑 기능은 매우 강력하므로 많이들 사용한다.

 

하지만 아래와 같이 a, b=b, a format은 안되는데 a=b는 적용이 되는 아이러니한 현상이 나타난다. 

 

* python의 index 함수는 리스트 내에서 특정 값의 위치를 찾아주는 함수이다. 

 

우리는 이러한 현상을 이해하려면 a, b= b, a의 적용 순서를 알아야 한다.

 

a[a.index(1)], a[a.index(2)]= 2, 1은 다음과 같이 기작한다.

index함수는 중복값이 있을 때 맨 처음 나타나는 값만을 return해준다. 

 

따라서 함수가 적용되지 않은게 아니라 같은 값이 있을 때 index함수가 맨 처음 위치값을 반환하므로,

 

결과값이 안바뀐 경우라 할 수 있다. 

 

그래서 입력값이 다른 경우에는 문제없이 적용이 된다.

 

 

무한 반복 생성

 

나는 다중 할당 정도로 생각을 하고 있었는데 propositional logic implications of the statement라고 문법이 있나보다.

 

집합론 관련 논리 내용 같은데 주제와 벗어나는 것 같기도 하고 잘 쓸 자신이 없어서 생략한다.

 

하지만 다중 할당의 순서는 알아두면 좋을 것 같다.

 

a, b={}, 5로 할 당하고(P) a[b]를 {}, 5로 할당(Q)하는 결과를 얻고 싶어한다. 

 

a = b = c 의 형태의 문법은 생각에 왼쪽에서 오른쪽으로 가거나 a=c & b=c형태로 진행 되어야 할 것이라 생각했다.

 

저자는 a=c & b=c를 원했던 것 같다. 우선 아래는 결과다.

 

왜 저렇게 무한 반복이 될까??

 

예상과는 다르게 코드는 위와 같이 진행이 되었다.

 

원했던 결과를 얻으려면 아래와 같이 결국 문법을 나누어 제시하여야 한다.

 

Hashable Object 문제

 

나도 hashable object가 뭔지 잘 몰랐다. 그래서 검색해보니 hash함수에 인자로 들어갈 수 있는 객체들을 의미한다.

 

대충 hash에 object로 올 수 있는 값들은 변경 불가능 한 값들이어야 한다. 

 

즉, int, bool, float, tuple, string, bytes 등은  hash에 object로 올 수 있는 반면,

 

list, dict, set 등은 hash에 object로 올 수 없다.

 

이 얘기를 왜하냐면.. 

 

우리는 dictionary의 key 값으로 unique한 값을 필요로 한다.

 

근데 보다시피 따로는 정수 1도, True 값도, 1.0도 key값으로 올 수 있다. 어찌보면 당연하다.

 

근데 죄다 어디로 사라지는지 모르겠다.

hash 값을 확인해 본 결과 모두 1값을 가졌다. 그래서 덮어써진 것 같다.

 

또한 hash 값이 같아서 ==연산자 결과가 같은 것을 볼 수 있다.

공집합은 모든집합의 부분집합이므로 False

 

all 함수는 object 내에 논리기호 모두 True 값을가져야 True를 반환 아니면, False를 반환하는 함수

 

수학적으로 왜 공집합이 False인지는 아직 이해가 가지는 않는데.. 일단 그래서 코드를 보기로 했다.

 

https://docs.python.org/3/search.html?q=all&check_keywords=yes&area=default

파이썬 공식 문서는 위와 같이 나타났다.

 

만약 입력자료 중 하나라도 not 연산을 했을 때 True가 되면(하나라도 False이면) False를 반환 아니면 True를 반환

 

공집합은 for문 자체가 실행이 안되므로, 그냥 True를 반환한다.

 

공집합은 논리상으로 False를 의미한다.

따라서 

all([])이 True였음에도 불구하고, [[]]는 [True]와 같음에도 False라는 이상한 결과가 나타난다.

 

이는 아래와 같이 코드가 돌기 때문이다.

 

 

이터레이터 관련 이슈

 

해당 이슈는 완전히 이해가 가지 않았으나, 사용도가 높지 않을 것 같아 결과만 첨부하였다.

 

sorted함수는 list를 반환하는데 reversed 함수는 iterator를 반환한다고 한다고 합니다.

 

reversed 함수는첫번째 호출에서 sorted함수를 호출한다고 합니다.  따라서 다음과 같이 공집합을 반환한다고 합니다.

 

논리 우선순위 문제

 

앞서 무한 반복 생성에서 봤듯이 python은 논리를 처리할 때 a == b in c이 있다고하면,

 

앞에서부터 연산을 하는게 아니라 a in c , b in c를 구한뒤 그 결과값이 a' , b' 이라고 하면 a'==b'을 진행하는 것 같다.

 

수학과 입장으로 참으로 마음에 안드는 결과가 아닐 수 없다.

 

객체 변환 문제

 

float과 int에 따른 논리 결과가 다른 문제를 보여준다.

 

본문에서 x<<y라는 operator가 있다.

 

이 operator는 비트가 y자리만큼 왼쪽으로 이동한 x를 반환한다는데 잘 모르겠다.

 

이것은 x에 2 ** y를 곱하는 것과 같은 결과라고 한다. 알고자 하는 결과와는 무관하니 넘어가자..

 

위와 같은 결과가 나오는 이유는

 

int와 float은 변환 과정에서 가까운 값으로 반올림하는 과정이 있다고 한다. 

 

int가 float 변환 과정에서 원래 값으로 변환이 된다고 하고,

 

float값에서 1을 더해줘서 원래 값이 나와야 하는데 뭐 반올림 과정에서 그대로 나타난다고 한다.

 

문제점을 알고 사용할수록 예기치 못한 에러를 이해하기 쉽다고 생각한다.

 

도움이 되기를 바란다. 

'python' 카테고리의 다른 글

코딩 스타일과 관리  (0) 2021.02.28
jupyter kernel 제대로 추가 안될 때  (2) 2021.01.24
python oracle DB 연동  (0) 2020.12.03
offline conda pack spyder설치 에러  (0) 2020.12.01
conda python 경로 인식못할 때  (0) 2020.11.28

[SMOTE] 알고리즘

통계2021. 2. 14. 16:00

SMOTE 알고리즘은 크게 오버 샘플링, 언더 샘플링, 취합 순으로 진행된다.

 

[오버 샘플링]

문자형이나 범주형 자료를 KNN에 사용하기위해 숫자형으로 변경

빈도가 가장 작았던 자료의 관측치수( rare : 50 -> nT)

 

300 개에 대해서 관측치별로 KNN 진행 #default 옵션은 5개 분류로 진행

 

자료를 6배 해야하므로 KNN 5개중 랜덤하게 1개를 뽑고 I 번째 값과의 차이(difs)에 랜덤균등분포(min=0, max=1)을 뽑아서 I 번째 자료에 더하여 자료를 생성하는 작업을 6번 함.

 

이렇게 300개의 빈도가 가장 작았던 자료를 KNN기반으로 오버샘플링을 진행

 

[언더샘플링]

빈도가 제일 작은 범주를 제외한 자료를 중복을 포함하여 언더샘플링 진행

 

[최종 아웃풋]

아래는 참고한 R 코드다. 

 

빈도가 낮은 경우가 많은 다범주에서 비율을 맞추기에는 다소 부적합해보인다. 

 

data(iris)

data <- iris[, c(1, 2, 5)]

data$Species <- factor(ifelse(data$Species == "setosa","rare","common"))

## checking the class distribution of this artificial data set

table(data$Species)

50

600

## now using SMOTE to create a more "balanced problem"

newData <- SMOTE(Species ~ ., data, perc.over = 600,perc.under=100)

table(newData$Species)

"위 코드 실행 코드"

#옵션 설정

form=formula('Species ~ .');data=data;perc.over = 600; perc.under = 100;

k = 5;learner = NULL#default

#target colum을 맨 마지막으로 정렬

## target column 위치(Species의 컬럼 위치 ==3)

tgt <- which(names(data) == as.character(form[[2]]))

## 빈도가 가장 작은 범주

minCl <- levels(data[, tgt])[which.min(table(data[, tgt]))]

## 빈도가 가장 작은 범주의 행의 index

minExs <- which(data[, tgt] == minCl)

## target colum의 위치가 마지막 컬럼이 아니면 마지막으로 위치 변경

if (tgt < ncol(data)) {

cols <- 1:ncol(data)

cols[c(tgt, ncol(data))] <- cols[c(ncol(data), tgt)]

data <- data[, cols]

}#newExs <- DMwR::smote.exs(data[minExs, ], ncol(data), perc.over, k)

Idata=data[minExs, ];tgt=ncol(data);N=perc.over;k=k

# INPUTS:

#비율이 가장 작은 변수 자료를 넣어서

# data are the rare cases (the minority "class" cases)

# tgt is the name of the target variable

# N is the percentage of over-sampling to carry out;

# and k is the number of nearest neighours to use for the generation

# OUTPUTS:

# The result of the function is a (N/100)*T set of generated

# examples with rare values on the target

# 문자열이나 범주형 자료를 KNN을 사용하기위해 숫자로 변경

nomatr <- c()

# 빈도가 가장 작은 범주 table n by m 이라면 NA로 된 n by m-1의 행렬 T 생성

T <- matrix(nrow=dim(Idata)[1],ncol=dim(Idata)[2]-1)

for(col in seq.int(dim(T)[2])){

if (class(Idata[,col]) %in% c('factor','character')) {

T[,col] <- as.integer(Idata[,col])

nomatr <- c(nomatr,col)

} else T[,col] <- Idata[,col]

}

# over sample의 비율인 N100보다 작다면 케이스를 줄여라# 본 분석에는 600이라 해당 없음

# 만약 50이였다면 N=50 (50/100)*150 150 개의 절반인 75개를 추출

if (N < 100) { # only a percentage of the T cases will be SMOTEd

nT <- NROW(T)

idx <- sample(1:nT,as.integer((N/100)*nT))

T <- T[idx,]

N <- 100

}p <- dim(T)[2]

nT <- dim(T)[1]

ranges <- apply(T,2,max)-apply(T,2,min)

#6 배로 sampling하게 조절 nrow=900

nexs <- as.integer(N/100) # this is the number of artificial exs generated

# for each member of T

new <- matrix(nrow=nexs*nT,ncol=p) # the new cases

for(i in 1:nT) {

# the k NNs of case T[i,]

xd <- scale(T,T[i,],ranges) #min max scaling

for(a in nomatr){

xd[,a] <- xd[,a]==0

}

#distance

dd <- drop(xd^2 %*% rep(1, ncol(xd)))

#현재 i 번째 값과 가까운 5knn

kNNs <- order(dd)[2:(k+1)]

#6 배 할 것이므로

for(n in 1:nexs) {

# select randomly one of the k NNs

neig <- sample(1:k,1)

ex <- vector(length=ncol(T))

 

# the attribute values of the generated case

# 해당값과 샘플링한 가까운 값의 차이를 컬럼별로 계산

difs <- T[kNNs[neig],]-T[i,]

#최소가 0 최대가 1인 균등분포 1개를 곱함 - 화이트노이즈의 개념인 듯

new[(i-1)*nexs+n,] <- T[i,]+runif(1)*difs

for(a in nomatr){

new[(i-1)*nexs+n,a] <- c(T[kNNs[neig],a],T[i,a])[1+round(runif(1),0)]

}

}

}#새로운 데이터를 통해 900 개 생성됨

newCases <- data.frame(new)

for(a in nomatr){

newCases[,a] <- factor(newCases[,a],levels=1:nlevels(Idata[,a]),labels=levels(Idata[,a]))

}

newCases[,tgt] <- factor(rep(Idata[1,tgt],nrow(newCases)),levels=levels(Idata[,tgt]))

colnames(newCases) <- colnames(Idata)

}#return newCases

newExs=newCases

if (tgt < ncol(data)) {

newExs <- newExs[, cols]

data <- data[, cols]

}

#빈도가 제일 작았던 범주를 제외한 자료를 중복허용해서 언더셈플링 비율*아까 오버샘플링 row

selMaj <- base::sample(x=(1:NROW(data))[-minExs],

size=as.integer((perc.under/100) * nrow(newExs)), replace = TRUE)

newdataset <- rbind(data[selMaj, ], data[minExs, ], newExs)

'언더샘플링/100* 오버샘플링 관측치 수=900'

'비율이 제일 작았던 범주 관측치 수 50'

'비율이 가장 작았던 자료를 KNN기반 오버샘플링 한 자료=900'

#1850

if (is.null(learner))

return(newdataset)

else do.call(learner, list(form, newdataset, ...))

}#######

 

'통계' 카테고리의 다른 글

기초통계분석  (0) 2019.07.07
감마분포 & 균일분포  (0) 2019.05.29
가설검정& 최강력검정  (0) 2019.04.25
R에서 일반화 가법모형(설명X)  (0) 2019.04.13

가상환경1을 jupyter notebook의 커널로 추가하고 싶었는데 

 

커널이 생기긴한데 제대로 추가가 되지 않는 현상이 있었다.

 

python -m ipykernel install --user --name [가상환경명] --display-name [보이는 이름]

 

증상은 해당 명령어로 설치를 했는데도 해당 tensorflow 버전이 나오지 않았다.

 

한동안 커널을 안쓰다가 불편해서 이를 고치고자 설정을 뜯어 보았다.

 

jupyter가 깔린 가상환경을 편의상 가상환경0 이라고 하자.

 

가상환경1에서 python에 위치를 확인하기 위해 which python을 입력

 

나는 /root/anaconda3/envs/가상환경명/bin/python에 존재했다.

 

이제 jupyter kernelspec list로 커널 리스트를 확인

 

확인해보니 가상환경1( 제기준 tf1_14)이 목록에는 존재했다. 

vi /root/.local/share/[가상환경0]/kernels/[가상환경1]/kernel.json

에 들어가서 

 

argv부분에 python 경로가 적혀있을 것이다.

 

근데 python 경로가 [가상환경0]에 대한 경로로 설정되어 있었다.

 

/root/anaconda3/envs/추가할 가상환경명(가상환경1)/bin/python으로 수정

 

:wq!

저장

 

python -m ipykernel install --user --name [가상환경명1] --display-name [보이는 이름]

등록!

 

될줄 알았는데 안된다.

 

가상환경1에 kernel이 안깔려있어서 그렇다..

 

conda activate 가상환경1

 

conda install ipykernel

 

이제 jupyter들어가보면 잘 될 것이다.

'python' 카테고리의 다른 글

코딩 스타일과 관리  (0) 2021.02.28
python을 시작하기 전에  (0) 2021.02.27
python oracle DB 연동  (0) 2020.12.03
offline conda pack spyder설치 에러  (0) 2020.12.01
conda python 경로 인식못할 때  (0) 2020.11.28

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

확인

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

확인

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.

확인