아이티윌_데이터 분석 55기/강의내용 필기_통계 및 분석

#5 5일차_적용함수, 자료구조

ecosso 2026. 4. 3. 16:00

01. 적용함수

 - 01 sapply

 - 02 apply

 - 03 mapply

 - 04 apply

 

02. 자료구조

 - 01 리스트

 - 02 matrix

 - 03 array


01. 적용함수

- for문 없이도 객체의 함수적용을 도와주는 성능이 좋은 함수
- 객체의 원소별, 행별, 열별로 분리 -> 함수적용 -> 적용결과를 알맞은 자료구조로 선택해서 리턴
- sapply, lapply, mapply, apply 

 - 01 sapply
 - 벡터의 원소별 함수의 적용
 - 벡터 인풋, 벡터 리턴


 - 02 apply
 - 2차원(행렬, 데이터프레임)에 적용
 - margin에 따라 행별, 열별, 원소별 적용 -> 벡터, 행렬 리턴


 - 03 lapply 
 - sapply처럼 벡터의 원소별 적용
 - 리스트로 리턴 


 - 04 mapply

mapply(function, # 적용함수
             ...)          # 함수가 필요로하는 객체를 순서대로 전달


 - 둘 이상의 벡터의 원소별 적용

 

# 예: 벡터의 원소별 반복

emp <- read.csv('emp.csv')
emp

str_to_upper(emp$ENAME)
str_sub(emp$ENAME, 1, 2)

# 급여등급 출력
if (emp$SAL >= 3000) {  # 불가
  'A'
} else {
  'B'
}


# 방법1) ifelse
ifelse(emp$SAL >= 3000, 'A', 'B')

 


# 방법2) ifelse
res <- c()
for (i in emp$SAL) {
  if (i >= 3000) {  
    res <- c(res, 'A')
  } else {
    res <- c(res, 'B')
  }
  }
res

 

 

# 방법3) 적용함수
f1 <- function(x) {
  if (x >= 3000) {  
    res <- 'A'
  } else {
    res <- 'B'
  }
  return(res)
}
sapply(emp$SAL, f1) # 벡터로 리턴

 


lapply(emp$SAL, f1) # 리스트로 리턴

 

 

 

# 연습문제 - 데이터프레임의 반복
# ','를 제거하고 숫자로 만들기 반복

card <- read.csv('card_history.csv', fileEncoding = 'cp949')

# x 자리에 스칼라, 벡터 모두 들어갈 수 있지만 데이터프레임은 들어갈 수 없음
# 따라서 apply를 통해 데이터가 쪼개서 들어가짐
f1 <- function(x) {
  as.numeric(str_remove_all(x,','))
}

apply(card[,-1], c(1,2), f1) # 정상(f1 함수에 스칼라 형태로 데이터가 전달)
apply(card[,-1], 2 ,f1)        # 정상(f1 함수에 벡터 형태로 데이터가 전달)

 

 

 

# 연습문제)
df1 <- read.csv('apply_test.csv')

# 1) deptno.name 컬럼값을 서로 다른 컬럼으로 분해
# deptno에는 10,20,30,1,1
# name에는 smilth, allen, ford, scott, word

 

### 풀이

# sol1 ) 정규식을 사용한 문자열 삭제
df1$deptno <- as.numeric(str_remove_all(df1$deptno.name, '-.+'))
df1$name <- str_remove_all(df1$deptno.name, '.+-')

df1[,c('deptno','name','X2010','X2011','X2012','X2013')] # 컬럼순서 재배치

 

 

 

# sol2) split
str_split('10-smith', '-') # 리스트로 리턴
str_split('10-smith', '-')[[1]][1] # "10"
str_split('10-smith', '-')[[1]][2] # "smith"

str_split(df1$deptno.name, '-')

f1 <- function(x) {
  str_split(x, '-')[[1]][1]
}

f2 <- function(x) {
  str_split(x, '-')[[1]][2]
}

df1$deptno <- sapply(df1$deptno.name, f1)
df1$name <- sapply(df1$deptno.name, f2)

df1

 

 

# 2) - 값을 모두 0으로 수정

 

# 값 전체 수정)
df1[(df1 == '-') & (!is.na(df1))] <- 0
df1[,-1] <- apply(df1[,-1], 2, as.numeric)

 

# 원소별로 수정 및 숫자 변환 동시에)
f1 <- function(x) {
  if ((x == '-') & (!is.na(x))) {
    return(0)
} else {
  return(as.numeric(x))
  }
}

df1[,-1] <- apply(df1[,-1], c(1,2), f1)
df1

 

 

# 3) 연도별 총합 출력
str(df1)
apply(df1[,-1], 2, sum, na.rm = T)

 

 

# 연습문제
# 30번 부서원이면서 SALESMAN 이면 급여의 10%
# 30번 부서원이면서 SALESMAN 아니면 급여의 8%
# 20번 부서원이면서 ANALYSY 이면 급여의 10% 나머지 8%
# 10번 부서원은 다 5%
emp <- read.csv('emp.csv')
# 방법1) ifelse
ifelse(emp$DEPTNO == 30, ifelse(emp$JOB == 'SALESMAN', emp$SAL * 0.1, emp$SAL * 0.08),
                         ifelse(emp$DEPTNO == 20, ifelse(emp$JOB == 'ANALYST', emp$SAL * 0.1, emp$SAL * 0.08),
                                emp$SAL*0.05))


# 방법2) for문
vbonus <- c()  # 초기화 필수!

for (i in 1:nrow(emp)) {
  if (emp$DEPTNO[i] == 30) {
    
    if (emp$JOB[i] == 'SALESMAN') {
      vbonus <- c(vbonus, emp$SAL[i] * 0.1)
    } else {
      vbonus <- c(vbonus, emp$SAL[i] * 0.08)
    }
    
  } else if (emp$DEPTNO[i] == 20) {
    
    if (emp$JOB[i] == 'ANALYST') {
      vbonus <- c(vbonus, emp$SAL[i] * 0.1)
    } else {
      vbonus <- c(vbonus, emp$SAL[i] * 0.08)
    }
    
  } else {
    
    vbonus <- c(vbonus, emp$SAL[i] * 0.05)
    
  }
}

vbonus

# 방법3) 적용함수

f_bonus <- function(DEPTNO, JOB, SAL) {
  if (DEPTNO == 30) {
    if (JOB == 'SALESMAN') {
      bonus <- SAL * 0.1
    } else {
      bonus <- SAL * 0.08
    }
  } else if (DEPTNO == 20) {
    if (JOB == 'ANALYST') {
      bonus <- SAL * 0.1
    } else {
      bonus <- SAL * 0.08
    }
  } else {
    bonus <- SAL * 0.05
  }
    return(bonus)
}

f_bonus(20, 'CLECK', 800)
mapply(f_bonus, emp$DEPTNO, emp$JOB, emp$SAL)


02. 자료구조

> 0. 스칼라
> 1. 벡터
 - 여러 상수를 나열해서 전달하기 위한 자료구조
 - 단 하나의 데이터타입만 허용
> 2. 리스트
 - key-value 자료구조
 - 각 층별 크기가 다른 벡터를 저장
> 3. 행렬 (matrix)
 - 1차원의 벡터가 2차원으로 확장된 구조
 - 하나의 데이터타입만 허용
> 4. 배열
 - 1차원의 벡터가 n차원으로 확장된 구조
 - 하나의 데이터타입만 허용
> 5. 데이터프레임
 - key-value 자료구조
 - 키 탐색을 용이하게 하기 위해 만든 자료 구조
 - 2차원
 - 각 컬럼별 서로 다른 데이터타입 허용

 

 - 01 리스트

# 색인
li[[1]]  # 벡터 리턴
li[1]    # 리스트 리턴


li[[1]][3] # 1층에 있는 3번째 원소 접근
li[1][3]   # NULL)li[1] 결과가 리스트이므로 [1]의 색인은 3층을 선택하는 의미를 가진다.


l2$a      # 이름으로 key(층) 선택 가능(벡터 리턴)
l2[['a']] # 이름으로 key(층) 선택 가능(벡터 리턴)
li['a']   # 리스트로 리턴

# 변경
l2$c <- 1:5  # 새로운 키 생성
l2$c <- NULL # 기존 키 삭제


# 예) l2 의 b를 B로 수정(일부 원소 수정)
l2[['b']]
l2[['b']][2] <- 'B'

l2

 


 - 02 matrix

# 1. 생성
matrix(data = , # 행렬로 표현할 대상(벡터)
       nrow = 1,# 행의 수
       ncol = 1,# 컬럼 수(nrow, ncol 둘 중에 하나만 전달 가능)
       byrow = F, # row 우선순위 여부
       dimnames = NULL) # 각 행과 열의 이름(리스트로 전달)

m1 <- matrix(1:12, nrow =3)
m2 <- matrix(1:12, nrow = 3, byrow=T)



# 2. 확인
nrow(m1) # 행의 수
ncol(m1) # 컬럼 수
dim(m1)  # 차원(3x4)
dimnames(m1) # 행과 열의 이름
colnames(m1) # 컬럼 이름
rownames(m1) # 행 이름

 

# 행과 열의 이름 변경
dimnames(m1) <- list(c('a','b','c'),
                     c('A','B','C','D'))
m1

 

# 3. 색인
m1[1,1]
m1[1,1:3]
m1[c(1,3),c(2,4)]
m1$A # 불가(key 구조가 아니므로 불가)

 

# column 하나를 선택할 때 1차원 벡터로 리턴되는 등 차원이 축소되는데,
# 이것을 방지해주는 옵션을 지정 가능하다
m1[,1]
m1[,1,drop=F] # 차원 축소 방지 옵션

 

 

 * 이후 예상치 못하게 차원이 축소되며 'dimension error' 등이 발생할 시 사용할 가능성이 있다.

 

# 변경
l2$c <- 1:5  # 새로운 키 생성
l2$c <- NULL # 기존 키 삭제

# 예) l2 의 b를 B로 수정(일부 원소 수정)
l2[['b']]
l2[['b']][2] <- 'B'

l2

 

# 4. 구조변경
m2 <- rbind(m2, c(100,200,300,400))
m2 <- cbind(m2, c(1000,2000,3000,4000))

m2 


m1 d라는 이름의 행 추가, 값은 자유. e라는 컬럼 추가. 값은 자유.

m1 <- rbind(m1, d = c(4,5,6,7))
m1 <- cbind(m1, e = c(13,14,15,16))
m1


# 5. 연산
m1 + m2 # 서로 크기가 같은 행렬끼리 사칙연산 가능


m3 <- matrix(1:4, nrow=2)
m4 <- matrix(seq(10,40,10), nrow=2)

m3 %*% m4

 


 - 03 array

 - n 차원
 - 행 -> 열 -> 층 순서로 자료 배치

  * python의 경우 층 -> 행 -> 열 순서

# 1. 생성
array(data = ,     # 대상
      dim = ,      # 크기(벡터로 전달)
      dimnames = ) # 행, 열, 층 이름

a1 <- array(1:16, dim = c(4,4))
a1

 


class(a1)

a2 <- array(1:24, dim=c(4,3,2))
a2

# 2. 색인
a2[,,1] # 첫번째 층 선택
a2[1,1,1] # 첫번째 행, 열, 층 선택