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] # 첫번째 행, 열, 층 선택

'아이티윌_데이터 분석 55기 > 강의내용 필기_통계 및 분석' 카테고리의 다른 글
| #7 7일차_데이터 형태 변환, NA 처리 (0) | 2026.04.08 |
|---|---|
| #6 6일차 : 외부파일 입출력, DB Connection, 기타 기능 (0) | 2026.04.07 |
| #4 4일차_집합연산자, 조건문, 반복문, 사용자 정의 함수, 적용함수 (0) | 2026.04.02 |
| #3 3일차_그룹핑, 조인, 정렬 (0) | 2026.04.01 |
| #2 2일차_벡터, 데이터 프레임, 문자열 함수 (0) | 2026.03.31 |