[R] for: 결과가 가변적일 때
for: 결과의 길이가 가변적일 때
책의 255쪽을 보면 for
의 몇 가지 변형에서 “결과의 길이가 가변적일 때”에 대한 설명이 있습니다. 이 부분에 대해 어떤 경우가 있는지 질문을 받기도 합니다.
물론 이 책은 특정한 분야를 상정하지 않았기 때문에 구체적인 예를 들지는 않았습니다. 최근 R Bloggers에는 적절한 예가 될 수 있는 문제가 실렸습니다. 이에 대해 얘기를 해보고자 합니다.
문제
raffleData <- data.frame("Name" = c("Elliot Morin",
"Caleb Murphy",
"Zoe Clark",
"Mason Li",
"Nathan Levesque",
"Colton Gagne"),
"Tickets_Bought" = c(5,10,9,12,3,6))
raffleData
## Name Tickets_Bought
## 1 Elliot Morin 5
## 2 Caleb Murphy 10
## 3 Zoe Clark 9
## 4 Mason Li 12
## 5 Nathan Levesque 3
## 6 Colton Gagne 6
문제는 간단하게 raffleData
를 다음과 같이 변환하는 문제입니다.
## Name Tickets_Bought
## 1 Elliot Morin 5
## 2 Elliot Morin 5
## 3 Elliot Morin 5
## 4 Elliot Morin 5
## 5 Elliot Morin 5
## 6 Caleb Murphy 10
## 7 Caleb Murphy 10
## 8 Caleb Murphy 10
## 9 Caleb Murphy 10
## ...
Elliot Morin
이 티겟을 5장 샀으니까 Elliot Morin
을 다섯 번 출력하려고 하는 것일까요? 어쨋든 티겟을 산(Tickets_Bought
) 만큼 자료를 반복해야 합니다.
결론적으로 R Bloggers는 다음과 같이 하고 있습니다.
res <- do.call(
rbind,
raffleData |> apply(1, function(x) replicate(x[2],x) |> t())
) |>
# Optional- coerce to data frame and coerce Tickets_Bought to numeric
as.data.frame() |>
within({
Tickets_Bought<- as.numeric(Tickets_Bought)
})
res |> head()
## Name Tickets_Bought
## 1 Elliot Morin 5
## 2 Elliot Morin 5
## 3 Elliot Morin 5
## 4 Elliot Morin 5
## 5 Elliot Morin 5
## 6 Caleb Murphy 10
하지만 개인적으로 눈에 익지 않네요. 책의 결과의 길이가 가변적일 때를 활용해봅니다.
이름만 출력하려면
# 데이터 프레임의 각 열별로 결과가 가변적이므로 결과를 저장할
# 리스트 `lst`를 마련합니다.
lst = vector("list", length=nrow(raffleData))
# 그리고 이름(Name)을 티겟을 구매한 횟수(Tickets_Bought)만큼 반복합니다.
for (i in 1:nrow(raffleData)) {
lst[[i]] = rep(raffleData[i, "Name"], raffleData[i, "Tickets_Bought"])
}
# 마지막으로 리스트의 내용을 벡터로 변환합니다.
result <- unlist(lst)
result |> head()
## [1] "Elliot Morin" "Elliot Morin" "Elliot Morin" "Elliot Morin" "Elliot Morin"
## [6] "Caleb Murphy"
이름과 티켓 장 수의 데이터프레임으로 만들려면
# 마찬가지로 결과가 가변적이므로 리스트 준비
lst = vector("list", length=nrow(raffleData))
for (i in 1:nrow(raffleData)) {
lst[[i]] = data.frame(
"Name" = rep(raffleData[i, "Name"], raffleData[i, "Tickets_Bought"]),
"Tickets_Bought" = rep(raffleData[i, "Tickets_Bought"],
raffleData[i, "Tickets_Bought"]))
# raffleData의 한 열에 대해
# 결과는 데이터 프레임으로 생성
}
result <- do.call(rbind, lst)
# 리스트의 데이터 프레임을 모두 합침
result |> head()
## Name Tickets_Bought
## 1 Elliot Morin 5
## 2 Elliot Morin 5
## 3 Elliot Morin 5
## 4 Elliot Morin 5
## 5 Elliot Morin 5
## 6 Caleb Murphy 10
그래서?
또 다른 R bloggers 포스팅을 보니 과연 누가 빠를지 의문이 생기네요.
library(microbenchmark)
library(ggplot2)
set.seed(2022)
mbm <- microbenchmark(
rblogger = {
res <- do.call(
rbind,
raffleData |> apply(1, function(x) replicate(x[2],x) |> t())
) |>
# Optional- coerce to data frame and coerce Tickets_Bought to numeric
as.data.frame() |>
within({
Tickets_Bought<- as.numeric(Tickets_Bought)
})
},
mineA = {
lst = vector("list", length=nrow(raffleData))
for (i in 1:nrow(raffleData)) {
lst[[i]] = data.frame(
"Name" = rep(raffleData[i, "Name"], raffleData[i, "Tickets_Bought"]),
"Tickets_Bought" = rep(raffleData[i, "Tickets_Bought"],
raffleData[i, "Tickets_Bought"]))
# raffleData의 한 열에 대해
# 결과는 데이터 프레임으로 생성
}
result <- do.call(rbind, lst)
}
)
mbm
## Unit: microseconds
## expr min lq mean median uq max neval
## rblogger 445.8 535.35 937.586 608.45 1001.75 6512.4 100
## mineA 4231.2 4871.75 8169.076 7297.70 9330.35 32520.5 100
autoplot(mbm)
## Coordinate system already present. Adding new coordinate system, which will replace the existing one.
뭔가 좀더 빠르게 바꿔보지만 replicate
과 apply
의 조합을 이기긴 힘들 것 같습니다. 역시 속도와 가독성은 상충하는 관계에 있네요.
library(microbenchmark)
set.seed(2022)
mbm <- microbenchmark(
rblogger = {
res <- do.call(
rbind,
raffleData |> apply(1, function(x) replicate(x[2],x) |> t())
) |>
# Optional- coerce to data frame and coerce Tickets_Bought to numeric
as.data.frame() |>
within({
Tickets_Bought<- as.numeric(Tickets_Bought)
})
},
mineB = {
lst_name = vector("list", length=nrow(raffleData))
lst_tickets = vector("list", length=nrow(raffleData))
#names = raffleData$Name
#tickets = raffleData$Tickets_Bought
for (i in 1:nrow(raffleData)) {
lst_name[[i]] = rep(raffleData$Name[i], raffleData$Tickets_Bought[i])
lst_tickets[[i]] = rep(raffleData$Tickets_Bought[i], raffleData$Tickets_Bought[i])
}
result <- data.frame(Name = unlist(lst_name),
Tickects_Bought = unlist(lst_tickets))
}
)
mbm
## Unit: microseconds
## expr min lq mean median uq max neval
## rblogger 487.9 930.75 1676.524 1071.15 1620.40 8889.1 100
## mineB 3734.7 7184.80 11185.158 9000.85 12844.25 42910.2 100
autoplot(mbm)
## Coordinate system already present. Adding new coordinate system, which will replace the existing one.
// add bootstrap table styles to pandoc tables function bootstrapStylePandocTables() { $('tr.odd').parent('tbody').parent('table').addClass('table table-condensed'); } $(document).ready(function () { bootstrapStylePandocTables(); });
Leave a comment