한의대 생활

mtcars
cars <- rownames(mtcars)
which(nchar(cars) == max(nchar(cars)))
cars[which(nchar(cars) == max(nchar(cars)))]

#### grep() ####
# 특정 문자열을 포함하는 데이터의 < 1) index  2) <문자열 전체>(value=TRUE)>를 반환한다.
# *** 주소에서 성북동을 포함한 주소를 검색하는 경우 등에서 사용된다.

# grep("문자열", data)
# grep("문자열", data, value = TRUE)
# ***대소문자 구분해버리므로 둘다 불러오고 싶다면 ***
# 1) grep("[vV]", data, value = TRUE)
# 2) 데이터 자체를 소문자나 대문자로 변환시킨 뒤 -> grep()을 쓴다.

# ***my)   cars %in% c("z") 는 포함관계가 아니라, 특정값 자체를 <or연산 : | | |>여러개 대신 연속(1:3)or이산(c(1, 3, 5)으로 쉽게 필터링에 쓸 수 있다. http://nittaku.tistory.com/352?category=764233

cars
grep("z", cars) # 1 2 --> 1번째 데이터, 2번째 데이터에서 가진다.
grep("z", cars, value = TRUE) # 실제 전체문자열을 보려면 value = TRUE 인자를 넣어준다.

grep("rd", cars, value = TRUE)

# ***대소문자 모두 포함된 문자열을 가져올 경우***
grep("[vV]", cars, value = TRUE)
# ***데이터 자체를 대문자 or 소문자로 바꾼 뒤 grep()하기 ****
grep("v", tolower(cars), value = TRUE)

grep("toyota", cars, value = TRUE )
grep("toyota", tolower(cars), value = TRUE )
grep("TOYOTA", toupper(cars), value = TRUE )


#### stringr 패키지 ####
#### ...L 1) str_count(df, "문자열") ####
# 문자열데이터 각각에 대해, 특정 문자열을  <몇개> 포함하고 있는지를 각 문자열데이터에 대한 벡터 반환해준다.
# *** 전체 자동차 리스트에서 sum()을 씌워, 해당 모델이 몇개 포함되어있는지 알 수 있다. ***
# *** 조씨 성을 가진 사람이 <몇명>인지 알 수 있다.

# str_count(df, "문자열")
# grep("문자열", df, value = TRUE)와는 인자가 순서가 다르다.

install.packages("stringr")
library(stringr)

cars
str_count(cars, "t") # (df, "문자열") -> 0 0 1 1 3 1 1 1 식으로 문자열 포함개수를 알려줌
str_count(cars, "o")

# 실전 - sum()을 마지막에 씌워, 전체 문자열데이터에서, 해당 브랜드 자동차모델이 몇개 가지고 있는지 알 수 있다.
str_count(toupper(cars), "TOYOTA") # 2개 발견
sum( str_count(toupper(cars), "TOYOTA") )

#### paste(,sep=" "), paste0( ) 함수 - 깨진 데이터, 끊긴 데이터를 합칠 때 ####
hi <-paste("hi", "Jack") # paste()함수는 붙혀넣기다.

# 끊킨 데이터를 붙혀주는 2가지 함수
# my) 구분자를 넣어서 붙히고 싶다면 paste(), 구분자 없이 바로 붙히려면 paste0()
paste("jac", "k") # "jac k"   ***paste()의 sep의 default는 " " 띄워쓰기 1칸이 있다.
paste("jac", "k", sep = "") # "jack"
paste0("jac", "k") # "jack"

paste("Hi", "jack", sep = ", ") # "Hi, jack"

#### 실전 응용 ####
#*** 1-1반부터 1-10반까지 붙혀넣어보자.
# 만약 모른다면,,,
c("1-1", "1-2", "1-3") #...
#my)...
global_object <<- character(10) # 전역변수는 생성시만 <<-      함수안이라도  대입은 <-
for(n in 1:10) {
   global_object[n] <- paste0("1-", as.character(n))
   }
global_object
#### *** paste() + 연속변수로 생성하면 ####
# "1"고정 / sep= "-" 구분자 / 나머지는 1:10까지 연속변수 생성
paste("1", 1:10, sep="-" )


# 따옴표를 제거하고 출력하기
a <- paste("The value of 'pi' is ", pi, ", endless!")
noquote(a) # 문자열에서 따옴표 제거하고 출력방법 1 ***
print(a, quote=FALSE) # 문자열에서 따옴표 제거하고 출력방법 1 ***



# rownames() 데이터에서 행이름만 가져오기
data("mtcars")
rownames(mtcars) # colnames는 자주썼었다.
#my) 행을 쉽게 보는 방법 t()
t(t(rownames(mtcars)))
colnames(mtcars) # colnames()

#nchar() 문자열 길이 확인( 문자열 벡터도 한번에 확인가능 )
nchar("조재성")  # nchar() 문자열 길이를 벡터로 나타내준다. -> 3
length("조재성") # length() 데이터(문자열 벡터)의 개수다.     -> 1
nchar(rownames(mtcars))

# which( 조건문 )로 가장 긴 이름 찾기 ********
# - which( 칼럼인덱싱 조건문 )으로 해당row를 반환하여 확인했었다. http://nittaku.tistory.com/336
# - which(sample[,1] == "ccd")
# 1) ncha()를 이용하여 문자열 길이를 벡터로 반환받기
# 2) nchar() == max( nchar() )를 조건문으로 해서
# 2) 문자열길이 벡터에 which( 조건문 )를 써서 가장 큰 값 받환하기

cars <- rownames(mtcars)
which( nchar(cars) == max(nchar(cars)) ) #16
cars[16]
# 한 문장으로 뽑아내기 - 인덱싱자리에서 넣기
cars[ which( nchar(cars) == max(nchar(cars)) )] # 1차원...

x <- 'what is your name?'
x <- "what's your name?" #작은따옴표를 안에 쓰려면 큰따옴표로

# character() 인자에 빈값을 넣으면?
y <- character() # my) character()를 사용하면 문자열 1차원 벡터가 생성
y #***character(0) = 빈값의 character 벡터라고 뜬다.
class(y) # 속성은 character
length(y) #길이(=데이터의 개수) 0인 character

# 케릭터 객체에 ""을 넣으면?
y2<-""
y2 # ""
class(y2) #속성은 character
length(y2) # ***length는 문자열 데이터의 개수... 문자열 길이 아님!***

length("1") # "1"도 길이가 1이지만,  ""도 길이가 1이다!! length는 문자열의 데이터의 개수 <---> nchar() 문자열 길이

y3 <- c("e", "12", "2") # 문자임

y4 <- character(10)
y4 # *** 큰따옴표가 10쌍이다.
# 3번째에다가 값을 넣어보자.
y4[3] <- "third"
y4 # "" "" "third" "" ...

# 10개까지만 있는 문자열 벡터에다가 12번째에 값을 넣어보면?
y4[12] <- "twelveth"
y4 # ***11번째에는 NA(결측치)가 차고,  12번째에 값이 들어간다
length(y4) # 길이는 12가 된다.

y4[11] <- "11"
y4


# 문자열인지 확인하기
n = 3
m = "3"
# 아래 부분이 필요한 것은 함수 속 if문...에서... 문자열/숫자 확인할 때!
is.character(n) # FALSE
is.character(m) # TRUE

class(n)
class(m)

#아래부분이 필요한 것은, 엑셀 등에서 가져온 자료가 숫자가 아니라 문자열일 경우 바꿔주기
n2 = as.character(n)  # 3 -> "3"
class(n2)

# 문자열은 1차원 벡터에 있어서, 자료형이 우선순위
t <- c(1:5) # 1, 2, 3, 4, 5

# 1차원 벡터에 있어서, 하나라도 문자열이 들어가면 모두 따옴표가 붙어 문자열이 되어버린다.
t2 <- c(1:5, "a")
t2 # "1", "2", "3", "4", "5", "a"

# 숫자 + logical을 섞는다면?
t3 <- c(1:5, TRUE, FALSE)
t3 # ***logical도 컴퓨터는 1, 0으로 알아듣는 숫자다
class(t3)


t4 <- c(1:5, TRUE, FALSE, "a")
t4 #문자열이 하나라도 섞이면 전부다 따옴표가 붙고, 문자열이 된다.
#***이 때, TRUE는 "1"이 아니라 "TRUE"로 바뀐다.


df1 <- data.frame( n       = c(1:5), #df는 각 요소의 개수가 딱맞는 사각형이어야한다.
                    letters = c("a", "b", "23", "1", "24") )
df1
# **** data.frame에서는 문자열을 "따옴표"로 표시해주지 않는다! ****
# ***data.frame의 형식을 문자열 따옴표를 보는 방법은 str()을 이용하는 것이다 ***
str(df1) # factor도 문자열을 포함한다?

df2 <- data.frame( n       = c(1:4, "a"), #df는 각 요소의 개수가 딱맞는 사각형이어야한다.
                    letters = c("a", "b", "23", "1", "24") )
str(df2) # data.frame도 칼럼별로 문자열이 자료형 우선순위를 가진다.

# my)***
df2 <- data.frame( n       = c(1:4, "a"), #df는 각 요소의 개수가 딱맞는 사각형이어야한다.
                    letters = c("a", "b", "23", "1", "24"),
                    stringsAsFactors = FALSE)
# 이전에 배운 sapply()로 칼럼별로 class를 본다.
apply(df2, MARGIN = 2, FUN = "class")
sapply(df2, "class")

install.packages("tidyr") # 정리할 때 좋은 패키지
install.packages("dplyr")# 자료를 빨리 찾아볼 때 좋은 패키지 (필터링 패키지)


library(dplyr)

# 데이터 준비
# 3가족이 사는데, f여자, m남자에 대한 이름과 나이 정보

member = data.frame(family = c(1, 2, 3),
                     namef  = c("a", "b", "c"),
                     agef   = c(30, 40, 23),
                     namem  = c("d", "e","f"),
                     agem   = c(44, 53, 25))
member
# data.frame의 현재형태는 각 가족에 대해서 보기 편할 수 도 있다.
# 예를 들어, 1번 가족에 a(30세), d(44세)가 있다. 이런식이다.
# 하지만, 데이터 분석에 있어서는 안좋다.
# 예를 들어, 여자멤버는 누가 있고, 나이에 따른 분포 등을 알기 어렵다
# 즉, 그래프를 그리기 쉽지 않다.

image


### 문제 : 데이터에서 namef, namem이 아닌 f/m를 따로 표시해서 알아보기 ###

#### 1. tidyr(타이디 r)을 이용해서 정리하기 ####
library(tidyr)
#### ...L 1) gather() ####
# 여러칼럼들을 칼럼들을 --> 1개의 칼럼key - value 값을 형태로 모아주는 함수
# gather( df, key*칼럼명 모을칼럼명, value*모을값의 칼럼명, 식별칼럼제외 모을 칼럼범위a:b)
# *** 이 때, 식별칼럼에 해당하는 family칼럼은 제외하고 모아준다. 만약 식별칼럼까지 모아버리면, 모으기전 같은 라인에 있던 이름-나이 의 연관성이 사라져버린다.

# *** reshape2패키지의 melt()와 매우 유사함.(http://nittaku.tistory.com/349)
# gather(membe(df), key(칼럼명모은 칼럼명), value(값 모은 칼럼명), family:agem) : 식별칼럼family까지 모으면, 모이기전의 값들이 연관성이 없어져버린다.
# ex> 1-namef-a / 1-agef-30 ==> namef-a 와 agef-30의 이름과 나이의 연관성이 사라져버림
gather(member, key, value, namef:agem)
a <- gather(member, key, value, namef:agem)
image

#### ...L 2) separate() ####
# 특정 칼럼의 값 -> 2개로 쪼개면서 2개의 칼럼 생성
# - gater로 모아진 데이터를 이용해서, 특정칼럼의 값을 쪼갠다.
# separte(값을 쪼갤df, 쪼갤칼럼명, c("쪼갠칼럼1", "쪼갠칼럼2"), 쪼갠위치)
b <- separate(a, key, c("variable", "type"), -1) # 뒤에서

image

#### ...L 3) spread() ####
# key-value형태의 칼럼을 --> key값을 기준으로 여러칼럼으로 생성 + value은 각각에 따라붙음
# ***reshape2의 dcast()와 비슷한 듯 : http://nittaku.tistory.com/349
# 여러칼럼들을 모아서 필요한 처리이후, 다시 name과 age를 분리하기
# spread(칼럼명/ 값 칼럼을 <칼럼명의 값의 이름으로> 2개칼럼으로 나눌df, 나눌 칼럼명, 따라붙을 값 칼럼)
new <- spread(b, variable, value)
image

### *** my) gather()와 spread()는 값은 따라이동하기만 하고, 칼럼을 뭉치고/나눈다. ***


#### 2. dplyr패키지의 filter()함수로 여자만 필터링하기 ####
## dyplr패키지의 filter함수는 벡터연산의 [df$칼럼명 , ]과 달리
## filter(df, 칼럼명 == )으로 바로 인덱싱 but 자동완성x http://nittaku.tistory.com/352
filter(new, type == "f")
filter(new, age >= 30)

image


#### 3. 만약, tidyr로 정리한 데이터가 아니라면?  ####
# (1) select( df, 가져올칼럼1, 가져올칼럼2, 가져올칼럼3)함수를 통해,
#     df에서 f붙은 특정칼럼들만 가져온다.
select(member, family, namef, agef)
# (2) 벡터연산[,]을 통해 열만 인덱싱해준다.
member[,c("family", "namef", "agef")]
# 그러나 특정나이 이상인 모든 사람들을 가져오게할 때.. 쉽지가 않다.
# agef, agem 칼럼이 2개이기 때문...
image


#### 4. 파이프라인(%>%)을 통한 tidyr 함수3개 연속 사용하기 ####
# 파이프라인을 사용할 때는, 아래와 같은 형식이며
# 각 함수에서 df는 생략된다.

new2 <- member %>%
         gather(key, value, namef:agem) %>%              #칼럼들을 key-value로뭉치기
         separate(key, c("variable", "type"), -1) %>%    #값을 2개 칼럼으로 나누기
         spread(variable, value)                         #key-value를 key값별로 칼럼나누
new2

image

주사위 1개 던지기

#### 주사위 던지기 uniform distribution ####

# 랜덤한 숫자를 뽑는다 다 확률이 동일 1/6

# p = 1/n, n of die = 6

# 랜덤으로 1~6사이 값을 나오게 runif()을 써서 만들고,

# 각 주사위 숫자가 몇번 나왔는지 table()함수로 확인하기

#runif() #랜덤한 값을 uniform으로 하겠다(확률이 다 똑같다)
runif(100) # 0부터 1사이 값을 100개 동일한 확률로 뽑는다.

# 다 소수점으로 나오니 1~6사이가 나오도록
# 1. 0 < x < 1 * 6
runif(1000)*6 # 0 < x < 6

# 2. 0을 배제하기 위해서 올림(ceiling)을 씀 - 반올림하면 0이 되어버린 경우가 있음.
ceiling( runif(10000) * 6 ) # 1<= x <= 6

# 3. 주사위 변수에 담음
die <- ceiling( runif(10000) * 6 )

# 4. table() - 각 요소의 빈도를 알려주는 함수
table(die)
# my) 동일한 확률(runif)로 나오니까,, 전체 빈도는 비슷하다.
# 100번, 10번만 던져보기
die <- ceiling( runif(100) * 6 )
table(die)
die <- ceiling( runif(10) * 6 )
table(die)

# 5. 몇번 던질지에 대한 변수를 생성해서 대입해주기
roll <- 100  # 몇번 던지는지 샘플을 뽑는 횟수
n <- 6      # 주사위와 동일한 경우의수
die <- ceiling( runif( roll ) * n )
table(die)
#my) 사용자 정의함수로 만들어보기
die_func = function(roll = 100, n = 6){
   die <- ceiling( runif( roll ) * n ) 
   print(table(die))
}
die_func(1000, 6)

# 6. barplot()으로 그려보기
b = table(die)
barplot(b)
# uniform distribution은 동일한 확률을 가지고 던진다는 의미(뽑는다는 의미)인데,
# 100번 던진 그래프로 확인해보니 완전히 동일하지 않다. 왜냐하면 횟수가 적어서
# 횟수를 늘일수록 전체의 경우의수는 각각이 동일해진다.

#my) 사용자 정의함수로 만들어보기
die_func = function(roll = 100, n = 6){
   die <- ceiling( runif( roll ) * n ) 
   print(table(die))
   barplot(table(die))
}
die_func(10, 6)
die_func(100, 6)
die_func(10000, 6)
die_func(100000, 6)


  • 10번 던졌을 때,
    image
  • 100번,
    image
  • 10000번,
    image
  • 10만번
    image


주사위 2개 던지기

#### 2개 주사위의 합 ####
# 1. 주사위 1개 10000번
roll <- 10000
dice <- ceiling( runif(roll)*6 ) # 1 ~ 6


# 2. 주사위 1개 더 추가해서 더하기
dice <- ceiling( runif(roll)*6 ) + ceiling( runif(roll)*6 ) # 2 ~ 12
table(dice)
# 눈으로 봐도, 가운데 쪽이 수가 많다..

# 3. 그래프로 분포 확인
a <- table(dice)
barplot(a)

## 10000번을 던졌더니, 7에 가장 분포가 많다.
# 2가 나올 경우의수는 (1, 1)이고, 12는 (6, 6)밖에 없다
# 가운데 7은 (1,6) (2,5) (3,4) (4,3) (5,2) (6,1) 6가지 경우의 수로, 2or12가 나올 경우의 6배다..


# 4. 확인해보기
# 2가 나오는 경우의수에서 (1,1) 각각 독립이고 확률이 같다.
# 7에서 (1,6)도 1/6*1/6이고, (2,5)도 1/6*1/6이다
# 2개 나올 경우의수에 단순하게 7배 된 것이 7이 나올 경우의수이다.

a["2"]
a[1] # 285
a["12"]
a[11] #300

a["7"]
a[6] # 1655 # 285 * 6 = 1710으로

# 5. 엄청나게 많이 던져보자
roll <- 1000000
dice <- ceiling( runif(roll)*6 ) + ceiling( runif(roll)*6 ) # 2 ~ 12
a <- table(dice)
a["7"] - (a["2"]*6) # 86차이 밖에 안난다.

image




주사위 3개 던지기


# 6. 주사위를 3개 던져보자.
roll <- 10000
dice <- ceiling( runif(roll)*6 ) + ceiling( runif(roll)*6 ) + ceiling( runif(roll)*6 ) # 3 ~ 18
table(dice)
a <- table(dice)
barplot(a)
# 중앙값인 10.5 근처의 10과 11에서 가장 경우의수가 가장 많고 비슷하다.
image

결측치

image


setwd('c:/users/is2js/R_da/')
#### 결측치(Missing value)처리 - [is.na(칼럼인덱싱), ]활용 / summary()활용 ####
# NA/NULL의 기본 형식으로 값이 비어있거나 다양한 형식(NILL)으로 숨어있는 값
# - 가장 재수없는 것은 .. 빈칸...


#### 데이터 준비 ####
data("Orange")
dim(Orange) # row와 칼럼수를 한번에 파악할 수 있다.
#nrow(Orange)
#ncol(Orange)

set.seed(1228)
# *** 모든칼럼에 대해 평등하게 랜덤 NA를 억지로 넣어주기 위해 행렬연산을 이용한다.***
# df를 matrix로 바꾸면, n X m 요소에 접근시 칼럼을 넘어가도 순서대로 붙어버린다.
# 그래서 1차원 벡터연산(1차원 인덱싱)으로만으로도 모든 요소에 접근이 가능해진다.
df = as.matrix(Orange)
# 1부터 행x열값까지의 수중에 랜덤하게 10개를 뽑아,
# 행인덱싱 자리에 넣어, 해당 행 전체에 NA를 집어넣는다.

df[sample(1:(nrow(df) * ncol(df)), size = 10) ] = NA
df = as.data.frame(df)
df


#### 1. is.na($칼럼인덱싱) 을 이용한 결측치 확인 ####
df[is.na(df$Tree), ]
df[is.na(df$age), ]
df[is.na(df$circumference), ]

df[(is.na(df$Tree))|(is.na(df$age)),]
df[(is.na(df$Tree))&(is.na(df$age)),]

# 값을 대입을 할려고보니, 오류가 난다.
df[is.na(df$Tree), "Tree"] = 123
# sapply(df, "class")로 속성확인! 을 해보니 factor였다.***
sapply(df, "class")

#### factor의 결측치 처리를 하는  -> NA에 넣을 값을 levels에 추가한다.. ####
# 1. 해당 칼럼인덱싱으로 levels확인
df[ , "Tree"] # 1,2,3,4,5 5개의 levels이 있다.
# 2. levels를 하나 더 추가해준다.
df[ , "Tree"] = factor(df$Tree, levels = c(1:5, 123))
df[is.na(df$Tree), "Tree"] = 123
# 3. 만약 다양한 levels이 적혀있다면, 직접 levels를 적어주기 곤란하다.
levels(df$Tree) # 이것을 factor의 levels에 적어준다.
df[ , "Tree"] = factor(df$Tree, levels = c(levels(df$Tree), 123))


#### 2. summary() ####
# - NA에 대해서만 칼럼별로 쉽게 파악 가능하다.
# - NULL 등은 따로 확인해야한다.
summary(df)

이상치

image

setwd('c:/users/is2js/R_da/')
#### 이상치(Outliers) ####
# - 데이터의 중심경향성에서   많이 떨어져있는 값
# - 이상치, 이상값, 특이치, 특이값
# - 처리절차 : 이상치 확인 -> 필터(또는색인) -> 처리방법 선정 -> 치환또는 제거
# - 처리방법 선정 :
# (1) 단순 제거 : 너무 적을 때
# (2) 대표값 치환 : 보너스 점수 받은 102점 -> 100점으로 치환
# (3) 통계기반 처리 : 평균값으로..
# (4) 머신러닝 기반 처리 : 남자의 평균, 여자의 평균.. 등

#### 데이터 준비 ####
set.seed(1228)
df = data.frame(xx = 1:200,  #xx에는 1부터 200까지
                 yy = rnorm(n = 200, mean = 5, sd = 3)) #yy에는 정규분포(rnorm) 개수 200, 평균5 표준편차 3짜리
head(df)

#### 1. 이상치 확인 ####
####...L (1) order()를 활용한 상위, 하위 3개값 추출하기 ####
# order(df$하위칼럼)을 행인덱싱자리에 넣어주면, 해당 칼럼 기준 오름차순으로 정리해준다.****
df[order(df$yy), ]
# 열인덱싱자리에 기준칼럼명을 인덱싱하여, 오름차순된 yy칼럼값을 뽑아낸다.
df[order(df$yy), "yy"]
df_sub = df[order(df$yy), ]
head(df_sub)

# 하위 3개 뽑기
df_sub[1:3,]      #하위 3개 row전
df_sub[1:3, "yy"] #하위 3개 yy값

# 하위3개(오름차순) 다시 한줄로 요약( 새로운 객체를 만들지 않는 장점!)
df[order(df$yy), "yy"][1:3]
# 상위3개(내림차순) 추출 : ***order(-특정칼럼) 형태로서, ()안에 -를 붙힌다.
df[order(-df$yy), "yy"][1:3]

# 상위 3개 추출값을 %in%을 이용해 매칭되는 row 추출해보기
df[ df$yy %in% ( df[order(-df$yy), "yy"][1:3] )   ,    ]
# 하위 3개도..
df[ df$yy %in% ( df[order(+df$yy), "yy"][1:3] )   ,    ]


####...L (2) quantile($인덱싱)을 통해 이상치 확인하기 ####
quantile(df$yy) # 제 4분위 수값을 알 수 있다.
quantile(df$yy, probs = 0.99)# 확률값을 넣어주면, 해당 %에 해당하는 값을 뿌려준다.
quantile(df$yy, probs = c(0.95, 0.99)) # 확률값은 c()를 이용해 여러개 줄 수 있음.
quantile(df$yy, probs = c(0.95, 0.99))[1]
quantile(df$yy, probs = c(0.95, 0.99))[2]
# 결과값을 보면, 99%라는 칼럼처럼 보이지만, name vector로서, name이 붙어있을 뿐,
# 가져오는 것은 벡터연산(인덱싱)으로 한다. [1] or [2]

aa = quantile(df$yy, probs = c(0.95, 0.99))
aa
names(aa) # name vector로서 95%, 99%가 있으나, 인덱싱은 벡터연산[1], [2]로..
aa[1]
aa[2]
# quantile의 결과값인 name vector에서 name을 제거 하고 싶다면
bb = as.numeric(aa)
bb

# ------예제 시작------
# 이제 quantile()을 통해 나온 분위수를 행인덱싱자리 조건문으로 넣어서 이상치 추출
# 상위 98% 백분위수보다 넘어가는 것만 추출해보자.( 2% 미만)
df[ df$yy > quantile(df$yy, probs = 0.98), ]

# 뽑은 것을 처리해보자.- 해당 칼럼에다가 값을 대입해주면 된다.
df[ df$yy > quantile(df$yy, probs = 0.98), "yy"] = 20
df[ df$yy > quantile(df$yy, probs = 0.98), "yy"]


#### ggplot()을 활용해서 확인 ####
library("ggplot2")
ggplot() +
   geom_point(data= rbind(df,
                          data.frame(xx = c(30, 50, 84),
                                     yy = c(35, 40, 39))),
              aes(x = xx,
                  y = yy),
              size = 7,
              alpha = 0.5,
              color = "#FFA500") +
   scale_y_continuous(limit = c(-10, 50))

#### 기본 boxplot()을 활용해서 확인 ####
boxplot(rbind(df,
               data.frame(xx = c(30, 50, 84),
                          yy = c(35, 40, 39))))

setwd('c:/users/is2js/R_da/')
#### 데이터 준비 ####
data("airquality")
df = airquality

#### 1. 단순 필터링(인덱싱, 벡터연산) ####
df_sub = df[1:4, ] # 전체에서 뽑아낼 때, 뒤에 _sub라는 접미사를 붙히는 버릇을 가지자.
df_sub

df[df$Day == 1, ]
df[df$Day != 1, ]

df[df$Day <= 2, ]

# 2개이상의 조건시 [()&(), ]
df[(df$Day == 1) & (df$Day == 2), ]
df[(df$Day == 1) | (df$Day == 2), ]


#### 2. %in% 연산자를 활용한 필터링 ####
# - %in% 1:2 1 or 2 포함 -> | 연산자를 여러개 사용할 필요가 없어진다.
# - 결과는 T / F 의 논리 연산자로 나옴. 앞에 !( )를 붙힐 수 있다. -> 행의 갯수를 nrow()할 필요없이 sum()으로도 바로 출력가능하다.

# %in%를 이용하여 or를 대신하기****
df$Day %in% 1:2 #조건문(mask개념)이다. --> TRUE or FALSE
df[df$Day %in% 1:2, ]

# 해당 조건을 만족하는 셀때는 nrow( %in% )한것을 할 필요 없이 sum()으로 바로 할 수 있다.
# row의 개수 빠르게 세기
sum(df$Day %in% 1:2) # %in%의 결과는 T/F이므로 sum은 1만 더해진 것이다. 값의 합이 아님.


#### 3. subset() 함수를 이용한 필터링 ####
# - 되도록이면 벡터연산(인덱싱)으로 해결하자.
# - subset(df, 조건문, select = c(컬럼명1, 컬럼명2)) # -컬럼명 : 해당 컬럼 제외 모든 컬럼
# - ***subset의 장점 : 칼럼명1:칼럼명3 으로 칼럼이름으로 연속하게 뽑아낼 수 있긴하다...
# - 사용하지말자


#### 4. dplyr패키지 - filter()함수로 필터링 : 조건문에 $없이 칼럼명만 사용(대신 자동완성x)한다. #####
# - filter(df, 단순칼럼명의 조건문)
# - dplyr는 속도가 빠르고 신뢰할만한 패키지다(bindr cpp -> c++을 사용하기 때문에)

install.packages("dplyr")
library("dplyr")
df = airquality

# 칼럼명만 변수처럼 바로 입력해서 조건문을 입력****
# - df$Day형태가 아니라 자동완성은 안되는 단점
filter(df, Day == 1)

filter(df, Day == 1 & Month == 5)

filter(df, Day %in% c(1, 3, 6)) # *** 1:3의 범위 뿐만 아니라 c(1, 2, 3)형태로 이산으로도 바로 or를 적용시킬 수 있다.!!!****

기초

setwd('c:/users/is2js/R_da/')
#### 사용자 정의 함수(user defined function) ####
# - 사용자 정의 함수를 모은 것이 패키지
# - apply() 관련함수와 같이 사용시 연산 소요시간 단축

#### 사용자 정의 함수 기본 ####
aaa = function(x){} # 함수명 = function(변수){} --> env창에 올라간다
aaa() # 사용은 함수명()으로 사용
aaa   # ()파라미터 입력을 안열어주면, 사용자 정의함수 자체가 나옴
aaa(x=123) # {}안에 함수가 없기 때문에, NULL값이 나옴

# 예제 01 - 입력값을 받지 않은 함수 - (x)가 없이 ()만
bbb = function(){
   print("Hi!")
}
bbb()
bbb(123) # 만약 인자를 억지로 넣어주면, 에러가 난다.


# 예제 02 - 입력값을 그대로 출력
# function(x){ print(x) } 처럼 {}안에서 x가 사용되는 순간, 함수 호출시에 반드시 파라미터가 포함되어야한다.
ccc = function(echo){
   print(echo)
}
ccc
ccc()
ccc(echo = "asdf")


# 예제 03 - 단순계산
ddd = function(x){
   x + 5
}
ddd( x = 10)
ddd( x ="계산인데 문자열을?" ) # 에러나옴
# **** 숫자를 문자열로 가지는 것까지 계산되게 할려면
ddd_upgrade = function(x){
  as.numeric(x) + 5
}
ddd_upgrade(x = "25")


# 예제 04 - 파라미터에 기본값을 지정해주기
# - ()안에 아무값이 없어도 기본값으로 작동함. 새로운 값이 들어가면 새로운 값으로
eee = function(x = 5){
   x^2
}
eee() # 기본값이 계산됨
eee(2)


# 예제 05 - 2가지 파라미터 넣어주기.
fff = function(aa, bb){
   aa*bb
}
fff(aa = 1, bb = 3)


# 예제 06 - {}안에 여러개 함수 호출하기
ggg = function(aa, bb){
   print(paste0("aa = ", aa)) # paste0를 이용하여, print문에 설명도 넣어주기
   print(paste0("bb = ", bb))
   print(paste0("aa * bb = ", aa*bb))
}
ggg(aa = 1, bb= 2)


응용

setwd('c:/users/is2js/R_da/')
#### udf 응용 ####
#### 캡슐화 ####
# - 코드를 묶어주는 것 -> source()를 활용한다.


#### 1. 할당연산자를 활용한 udf ####

# - 할당연산자(<<-)를 이용하여 전역변수도 생성한다
# - return을 이용하여 값을 반환한다(print에서 벗어나...)
# 예제 1
# 1) 들어온 x를 3번 반복한다음, 각각을 "-"로 연결한 뒤, global_object에 넣어준다.
# 2) 들어온 x를 nchar()를 이용해 문자열 개수를 return하자.

udf = function(x){
   # paste()에서 collapse = 로 이어준다. <-> 구분자는 sep =
   global_object <<- paste(rep(x, 3), collapse = "-" ) # 할당연산자 = 를 사용하면 전역변수가 인식이 안된다..
   return(nchar(x))
}
udf("a")
global_object

udf("asdfasfda")
global_object # 하지만, 기존에 사용하던 변수를 덮어쓸 수 있으므로, 전역변수+할당연산자를 쓰는 것은 위험할 수 있다.


# 사용자 정의 함수 안에서, 여러 개의 자료형태를 반환해주고 싶을 때 list()를 사용하면 된다.
udf2 = function(x){
   aa = paste(rep(x, 3), collapse = "-")
   bb = nchar(x)
   cc = rep(x, 3)
   print(aa)
   print(bb)
   print(cc)
}
udf2("asdf") # 이런식이면, print는 3개가 될지라도, 변수에 할당시 마지막 값만 대입된다.
f = udf2("asdf")
f # 마지막 cc만 대입이 되어버린다. 묶여있는 구조가 아니라서..

# 여러 자료를 return시 list()를 이용하면 된다.
udf3 = function(x){
   aa = paste(rep(x, 3), collapse = "-")
   bb = nchar(x)
   cc = rep(x, 3)
   return(list(aa, bb, cc))
}
l = udf3("asdf")
l
l[[1]]
l[[3]]


#### 2. apply( FUN = "udf")함수를 활용한 udf ####
# 예제1) apply()없이.. udf로 들어온 x를 문자열로 바꾸고, 문자열 갯수를 반환하여 칼럼 생성
udf_noapply = function(x){
   return(nchar(as.character(x)))
}
data("iris")

iris[,"n_char"] = udf_noapply(iris$Species)
head(iris)

# 하지만, 여기서 문자열개수를 +1 정도 추가하려면 또 함수를 적어야한다
iris[,"n_char"] = udf_noapply(iris$Species) + 1
head(iris)
# 이때는, 사용자 정의 함수의 마지막 return 에 + 1 해주는 식으로 실수를 줄이자.

# 예제2)
# 1) 먼저 데이터를 df로 만든 다음(apply()에는 df를 넣더라)

df = head(iris)
# 2) 숫자정보만 빼낸 다음, 세로방향2으로 적용시키면
# FUN = 인자에 "사용자 정의함수" 를 적용시키면, df전체에 문자열 갯수가 반환되서 대입된다.
apply(X = df[1:4], MARGIN = 2, FUN = "udf_noapply" )

setwd('c:/users/is2js/R_da/')

# for 반복문은 쉽지만, 속도가 느리다는 단점이 있다. 그것을 대체할 함수가 2개이다.
# 1. apply()
# 2. ifelse()

# ifelse( test = 특정 칼럼(?)에 대한 조건, yes = 참일 경우, no = 거짓일 경우 ) 의 형태이다.
image

# apply() -> 파생된 함수 lapply(), sapply() 등
# - apply( X = df, MARGIN = 1, FUN = "sum")
# apply()함수는 MARGIN인자가 계산 방향이다.
# MARGIN = 1 : 가로방향(행 방향)  #(파이썬은 세0가1 , R은 세2가1)
# MARGIN = 2 : 세로방향(칼럼 방향)

image

# sapply() : apply() + MARGIN = 2(세로방향) 의 함수다. (세2가1)
# 엄청 자주 사용하는 함수이다. 세로방향 s!
# - sapply(df, "sum")   : 칼럼별로 합계 확인
# - sapply(df, "class") : 세로방향(칼람별로) 속성확인


실 습


set.seed(1228)
df = data.frame(aa = 1:6,
                 bb = sample(c("a","b"),
                             size = 6,
                             replace = TRUE)) # 2개인데도 복원추출 시켜서 6개 뽑기
df

#### ifelse() - 기준칼럼을 가지고 조건을 걸어서--> 해당칼럼에 y/n선택적 대입 ####
df[,"col_1"] = ifelse( test = df$aa == 3, # test = 기준칼럼에 대한 조건
                        yes  = 33333,       # yes = 참일 경우 대입할 값
                        no   = "무관심")    # no  = 거짓일 경우 대입할 값
df
# 만약 for문으로 짠다면?
for( n in 1:nrow(df)){
   if(df$aa == 3 ){
     df[n,"col_1"] = 33333
   }else{
     df[n,"col_1"] = "무관심"
   }
}

# ifelse()중첩해서 사용해보기
df[,"col_2"] = ifelse( test = df$aa == 3, # test = 특정칼럼에 대한 조건
                        yes = 33333,       # yes = 참일 경우 대입할 값
                        no  = ifelse( test = df$aa == 4,
                                      yes  = 44444,
                                      no   = "무관심"))   
df

# 만약 yes부분(33333, 44444)가 동일한 상태라면, 중첩을 쓰지 않고
# 1. no부분을 먼저 값을 채운 뒤
df[, "col_3"] = "무관심"
# 2.행 자리에 <칼럼인덱싱 %in% 범위(3:4)> 를 통해 더 간단히 대입할 수 있다.
df[df$aa %in% 3:4, "col_3"] = "오..."
df



#### apply() - 칼럼별(세2) or 행별(1)로 계산시 빠르다.#####
data("iris")
head(iris)
# 1. colnames() + t(t()) 를 이용하여 칼럼명 쉽게보기
t(t(colnames(iris)))
# 2. apply()는 계산하는 함수이므로, 숫자데이터만 남긴다.
df = head(iris[1:ncol(iris)-1])
# 3. 가로방향 sum
apply(X = df, MARGIN = 1, FUN = "sum")
# 4. sum을 새로운 칼럼으로 추가
df[,"sum"] = apply(X = df, MARGIN = 1, FUN = "sum")
df
df[,"mean"] = apply(X = df, MARGIN = 1, FUN = "mean")
df
# 5. 칼럼별(세2) 평균
apply(X = df, MARGIN = 2, FUN = "mean")
# 6. 소수점이 길면 반올림
round(apply(X = df, MARGIN = 2, FUN = "mean"), 4)

#### sapply() - s는 세로방향 세2 Margin2 생략 ####
sapply(X = df, FUN = "mean")
# 가장 많이 쓰는 sapply() - FUN = "class" ****
sapply(X = df, FUN = "class")
# 칼럼명뿐만 아니라 칼럼 속성도 쉽게 보기
t(t(sapply(X = df, FUN = "class") ))
# 파라미터를 생략해도된다.
t(t(sapply(df,"class") ))

setwd('c:/users/is2js/R_da/')

#### Pivoting ####


# 1. reshape2 패키지 설치
install.packages("reshape2")

set.seed(123)
df = data.frame(Obs = 1:4,
                 A = sample(10:99, size = 4),
                 B = sample(10:99, size = 4),
                 C = sample(10:99, size = 4))
df
image


# 2. melt()함수를 통해 여러개의 칼럼을 --> 한칼럼에 세로로 늘어트린다.
# - melt(df, id.vars = "식별 칼럼명Obs", Variable.name = "여러개의 칼럼을 하나로 뭉친 칼럼명", value.name = "각 값들이 나올 칼럼명")
library("reshape2")
df_melt = melt(df, id.vars = "Obs",
                variable.name = "Group", value.name = "Count")
df_melt
image
# 만약 variable.name(뭉친칼럼명)과 value.name(값 칼럼명)을 안넣어주면 default한 칼럼명(variable칼럼 / value칼럼)이 생긴다.
df_melt_default = melt(df, id.vars = "Obs") # 식별 칼럼명도 안넣어주면, 그것까지 뭉친칼럼으로 들어간다.
image


# 3. dcast()와 acast()
# melt형태의 df를 값별로 여러개의 칼럼을 만드는데
# 추가적으로, melt된 형태의 데이터를 dcast()를 통해 여러개의 칼럼으로 흐트려 놓으면 각 칼럼별 NA값을 쉽게 확인할 수 있다.
# 게다가    , fill = 인자를 통해 결측값을 채울 수 도 있다.
# 게다가    , fun.aggregate = length(개수),sum(합) 등 요약 정보도 확인할 수 있다.
#  - dcast(df_melt, formula = 식별칼럼명 ~ 뭉친칼럼명, value.var = "값을 가진 칼럼명")
#  - acast(df_melt, formula = 식별칼럼명 ~ 뭉친칼럼명, value.var = "값을 가진 칼럼명")
#   > dcast와 동일하나, 식별칼럼Obs를 없앤 df를 생성
dcast(data = df_melt, formula = Obs ~ Group, value.var = "Count")
image
acast(data = df_melt, formula = Obs ~ Group, value.var = "Count")
image

# 데이터를 일부 뺀상태에서 dcast()하여 NA로 만들고 관찰하기
dcast(data = df_melt[-(2:5),], formula = Obs ~ Group, value.var = "Count")
image
# - 세로로 녹여진 df_melt의 2행~5행까지를 빼먹고 dcast()하면,
# - dcast()결과는 각 칼럼별로 결측값을 쉽게 확인할 수 있다.
dcast(data = df_melt[-(2:5),], formula = Obs ~ Group, value.var = "Count",
       fill="NA래요!")
image



# 4. t()함수 2번 감싸주기를 이용한 1차원 행렬(1,n) --대각선기준 접기--> n차원 행렬(n,1)로 나타내면
# - n행이 되므로 colnames()같은 정보를 한눈에 쉽게 볼 수 있다.
?t
data("CO2")
head(CO2)

df = cbind(CO2, CO2) # cbind()로 칼럼수가 여러개가 되도록 임의 조정
df
colnames(df)

# 그냥 colnames개수만큼(length(colnames(df)) or ncol(df))
# paste0() + letters를 이용하여 다른걸로 바꿔주자.
colnames(df) = paste0("CO2_", sample(LETTERS,  length( colnames(df) )     ))
colnames(df)
head(df)
ncol(df)
# *** 일반 원소에 t()를 2번 감싸면 세로로 쭈욱 생긴다.
t(colnames(df))
image
t(t(colnames(df)))
image

프로젝트를 통한 관리

#### 1. 캡슐화 ####
# 코드를 나누어서 덩어리로 관리
# 덩어리들이 따로 있으니까 기능 추가/보수도 쉽다.

#### 2. R Studio 기능 활용 ####
# 주석 4개로 접고 펴기 등 -> [ Ctrl+shift+O ]로 다시 메뉴 불러옴

#### 3. 사용자 정의함수 활용 ####
# source()를 활용하면, 다른 스크립트의 함수를 불러올 수 있다.
# 사용자 정의 함수 -> 스크립트로 저장 -> 다른데서 source()로 불러오기(사용자 정의함수 자동 등록됨)

#### 4. 파일을 목적별로 나누기 ####
# 용도에 따라 : 데이터 입출력 / 전처리 / 시각화/ 레포팅ppt, markdown 등
# 성질에 따라 : 환경설정 / 데이터 병합 / global option 등
# 특정 업무 처리
# ex>
# 01_env.r   : 패키지 로딩 / 옵션 설정 / DB연결
# 02_EDA.r   : 파일 불러오기 / 이상치/결측치 처리 / 데이터 분포 확인 / 상관계수, 행렬 등
# 03_model.r : Feature Engineering / 모델 성능비교
# 04_vis.r   : 시각화 공들여서 할 때,

#### 5.Project 생성 및 관리 ####
# 여러 스크립트 파일과 작업폴더를 한번에 관리
# 작업 폴더 관리가 매우 쉬워짐
# New Project > Empty Project 등


image

image

image
- New project를 선택해서 새로운 프로젝트 생성


image
- 윈도우에서 ~ 표시는 문서(Document)폴더를 의미한다.
- 나는 Browse를 통해 특정 폴더를 지정해주었다.

image

image
- getwd()결과 자동으로 해당 폴더의 directory가 working directory로 지정되어있다!


image
- 주석을 앞뒤로 # 4개씩 달고, 하위 주석일 경우 ...L 1) (엘 대문자) 식으로 만들자.
- [ Ctrl + Shift + O ]를 누르면 우측에 주석 메뉴가 뜬다.!

image

image
image
- row번호 우측에 화살표를 접고 펼수 있다.


사용자 정의함수로 스크립트 캡슐화 -> source()로 활용하기


image
- 사용자 정의 함수를 정의한 .r파일을 하나 작성하여 저장 한 뒤

image
- source(" .r")만 호출하여도 env창에 사용자 정의함수가 등록이 되는 것을 확인할 수 있다.

image
- 정상적으로 사용가능하다.

6. R Clean code 작성법

2018. 12. 29. 12:43

setwd('c:/users/is2js/R_da/')
# 1. 연산자 좌우 띄어쓰기
# 2. 쉼표 다음 띄어쓰기 or 줄바꿈
# 3. for문 길어질때 마지막 주석으로 표시해주기
# 4. 너무 많은 code는 Code > Reformat code [ Ctrl + Shift + A]

install.packages("ggplot2")
library("ggplot2")

data("iris")
df = iris

ggplot() +
   geom_point(data = df,
              aes(x= Sepal.Length,
                  y= Sepal.Width,
                  color = Species),
              size = 3)

5. 속성 확인 및 변환

2018. 12. 29. 12:42

setwd('c:/users/is2js/R_da/')
#### 원소<벡터<리스트 속성확인 ####
# class()
# if문에서 T/F 확인은 is.numeric() / is.character() / is.data.frame() / is.list()

#### 원소<벡터<리스트 속성확인 ####
# as.numeric() / character()
# 문자열 속에 숫자가 있다면 가능
# 문자열 속에 문자가 있다면 -> NA(에러)가 남.


#### 데이터 준비 ####
aa = 1234
bb = "test"
cc = TRUE
dd = NA


class(aa)
class(bb)
class(cc)
class(dd) # NA는 logical이다.
class("NA") # 문자열은 그냥 character

is.numeric(aa)
is.numeric(bb)
is.numeric(cc)
is.numeric(dd)

is.character(aa)
is.character(bb)
is.character(cc)
is.character(dd)

aaa = 1:5
bbb = letters[1:5]
ccc = data.frame(a = aaa, b = bbb)
ddd = list(asdf = aaa, qwer = bbb)

class(aaa)
class(bbb)
class(ccc)
class(ddd)

is.numeric(aaa)
is.integer(aaa)
is.data.frame(ccc)
is.matrix(ccc)
is.list(ddd)

#### 속성 변환하기 ####
aaa = 1:5
bbb = letters[1:5]
ccc = data.frame(a = aaa, b = bbb)
ddd = list(asdf = aaa, qwer = bbb)

as.character(aaa) # 전부 따옴표가 붙는다.
as.numeric(bbb) # 문자는 숫자로 못바꿈 -> 각각이 NA 처리
as.numeric(c(123, "345", "ddd")) # 1차원 combine에서는 문자가 우선이라서 character순서를 가지고 있다. 문자열속 숫자만 numberic으로 변형된다.

as.matrix(ccc) # data.frame은 사각형을 유지한 상태이므로 matrix로 변형된다.
# matrix는 행이 [1,] [2,] 형태 / 원소는 ""로 나타남.



#### factor ####
# 다루기 힘들며, 익숙하지 않으면 피해야한다.
# 통계처리 전에 사용된다.
#factor는 1차원 벡터와 함께 levels로 우선순위를 둔다
.
fac = factor(c(13, 22, 88), levels = c(88, 22, 13))
fac

#숫자로 구성되더라도 as.numeric()으로 변환하면, 우선순위만 표기된다.
as.numeric(fac) # 13, 22, 88 -> levels기반 우선순위 3, 2, 1 로 출력됨
# factor 숫자원소를 실제 숫자로 바꿀려면, as.character() -> as.numeric() 순으로 적용해야한다.
as.numeric(as.character(fac))

# 만약 levels를 주지 않는다 하더라도, 첫번째부터 우선순위가 적용되어, 1, 2, 3형태로 뽑아진다.
fac_2 = factor(c(13, 22, 88))
as.numeric(fac_2)


#### table #### - data.frame 칼럼 2개짜리에 대응하여 행/렬로 나타낸 뒤 빈도를 나타내준다.
# 2개 칼럼짜리 df 를 만들고 table()로 감싸주어 테이블을 만들자.
df = data.frame(var1 = rep(letters[1:2], 3),
                 var2 = rep(1:3, 2))
df
df_table = table(df)
df_table
class(df_table)

# 다시 data.frame으로 바꾸면 빈도칼럼이 생긴다.
as.data.frame(df_table)

setwd('c:/users/is2js/R_da/')


#### 데이터 준비하기 #####

data("iris") # R에 포함된 iris데이터를 가져온다. 자동으로 iris 변수에 들어가진다.
head(iris) # 따옴표 아님!

data() # R에 깔린 data 확인해보기
data( package = .packages(all.available = TRUE)) #패키지들에 깔린 모든 데이터셋 가져와보



#### 데이터 저장하기 ####


#### write.csv() #### - csv를 sep인자 없이 바로 저장
# csv load/save 고정!!!! #
# read.csv() : stringsAsFactors = FALSE      - 문자열 factor로 인식안하도록
# write.csv() : row.names = FALSE             - 행번호들 저장안하도록

write.csv(iris, "iris_csv.csv" , row.names = FALSE)
?write.csv #설명보기

#### write.table() #### - csv/tsv 등 다 가능 with 구분자
write.table(iris, "iris_tsv.tsv", row.names = FALSE,
             sep="\t")
# csv나 tsv가 아닌 파일들은 txt + 다른구분자로 저장
write.table(iris, "iris_txt.txt", row.names = FALSE,
             sep="@#$%")


#### jsonlite - write_json() ####
# json은 형식을 갖추고 있으므로, 용량이 큰 편이다.
library("jsonlite")
write_json(iris, "iris_json.json")

#### excel_link - xl.save.file() ####
# 현재 R 3.5.2버전에서는 설치가 안된다.
install.packages("excel_link")
library("excel_link")
xl.save.file(r.obj = iri, filename = "iris_xlsx.xlsx",
              row.names = FALSE)

+ Recent posts