按顺序迭代 R 中的两个列表
Iterate sequentially over two lists in R
我有两个看起来像这样的 df
library(tidyverse)
iris <- iris%>% mutate_at((1:4),~.+2)
iris2 <- iris
names(iris2)<-sub(".", "_", names(iris2), fixed = TRUE)
我的目标是减少 iris
中高于 iris2
中相应变量最大值的变量值,以匹配 iris2
中的最大值。
我已经编写了一个函数来执行此操作。
max(iris$Sepal.Length)
[1] 9.9
max(iris2$Sepal_Length)
[1] 7.9
# i want every value of iris that is >= to max value of iris2 to be equal to the max value of iris 2.
# my function:
fixmax<- function(data,data2,var1,var2) {
data<- data %>%
mutate("{var1}" := ifelse(get(var1)>=max(data2[[var2]],na.rm = T),
max(data2[[var2]],na.rm = T),get(var1)))
return(data)
}
# apply my function to a variable
tst_iris <- fixmax(iris,iris2,"Sepal.Length","Sepal_Length")
max(tst_iris$Sepal.Length)
7.9 # it works!
我面临的挑战是我想按顺序迭代我的函数超过两个变量列表 - 即Sepal.Length
Sepal_Length
、Sepal.Width
和Sepal_Width
等
有人知道我该怎么做吗?
我尝试使用 Map
但我做错了。
lst1 <- names(iris[,1:4])
lst2 <- names(iris2[,1:4])
final_iris<- Map(fixmax,iris, iris2,lst1,lst2)
我的目标是获得一个 df (final_iris
),其中每个变量都已使用 fixmax
指定的标准进行了调整。
我知道我可以通过 运行 在每个变量上设置我的函数来做到这一点。
final_iris <- iris
final_iris <- fixmax(final_iris,iris2,"Sepal.Length","Sepal_Length")
final_iris <- fixmax(final_iris,iris2,"Sepal.Width","Sepal_Width")
final_iris <- fixmax(final_iris,iris2,"Petal.Length","Petal_Length")
final_iris <- fixmax(final_iris,iris2,"Petal.Width","Petal_Width")
但在实际数据中,我必须 运行 这个操作数十次,我希望能够按顺序循环我的函数。
有谁知道我如何按顺序在 lst1
和 lst2
上循环我的 fixmax
?
这是您所期待的吗?
my_a <- iris %>% mutate_at((1:4),~.+2)
iris2 <- iris
names(iris2)<-sub(".", "_", names(iris2), fixed = TRUE)
my_var <- which(my_a$Sepal.Length >= max(iris2$Sepal_Length) & my_a$Sepal.Width >= max(iris2$Sepal_Width))
if (length(my_var)) {
my_a <- my_a[my_var,]
}
您的问题很可能与数据框本身就是列表这一事实有关。 Map()
期望非函数参数是相同长度的列表。任何比最长列表短的参数都会被“回收”以匹配它的长度。
目前,您有:
final_iris<- Map(fixmax,iris, iris2,lst1,lst2)
这实际上等同于:
final_iris<- Map(fixmax,
list(iris$Sepal.Length,
iris$Sepal.Width,
iris$Petal.Length,
iris$Petal.Width,
iris$Species),
list(iris2$Sepal_Length,
iris2$Sepal_Width,
iris2$Petal_Length,
iris2$Petal_Width,
iris2$Species),
lst1,
lst2)
我怀疑您希望将 iris
和 iris2
提供给对 fixmax()
的每次调用。为了让 Map()
像这样回收它们,它们需要是单元素列表。那就是你可能想要的:
final_iris<- Map(fixmax, list(iris), list(iris2),lst1,lst2)
要将数据帧列表合并为一个数据帧,请执行
do.call(rbind, final_iris)
你的函数乍一看似乎很复杂,难以阅读。我们可以使用快速函数
为列中的每个值整理函数 return max(x, max_val)
#function to correct max
adjust_max <- function(x, max_val) {
return(ifelse(x >= max_val, max_val, x))
}
最后,我们希望使用这两个数据帧自动并按顺序应用它。我们将使用一个简单的 for 循环。附上设置问题的代码。
#libraries
library(tidyverse)
#set up fake data
iris_big <- iris%>% mutate_at((1:4),~.+2)
iris_small <- iris
names(iris_small)<- sub(".", "_", names(iris_small), fixed = TRUE)
#check which is the bigger one and the smaller
max(iris_big$Sepal.Length) #bigger
max(iris_small$Sepal_Length) #smaller
#function to correct max
adjust_max <- function(x, max_val) {
return(ifelse(x >= max_val, max_val, x))
}
#apply it to get a final result
iris_final <- iris_big
# iterate over columns, assuming same positions
# you can edit the 1:ncol(iris_final) to only take the columns you want
for (i in 1:ncol(iris_final)) {
#check numeric
if (is.numeric(iris_final[,i])) {
#applies the function - notice we call iris_final and iris_small
iris_final[,i] <- sapply(iris_final[,i],
adjust_max,
max_val = max(iris_small[,i]))
}
}
#check answer is correct
apply(iris_final[,1:4], 2, max)
apply(iris_small[,1:4], 2, max)
tail(iris_final)
您应该考虑使用列索引;一个完整的(不包括数据框架结构)基础 R 解决方案可能如下所示:
# Resolve the indices of the numeric vectors in
# iris: num_cols => integer vector
num_cols <- which(
vapply(
iris,
is.numeric,
logical(1)
),
arr.ind = TRUE
)
# Map the pmin function over iris to select the
# minimum of the vector element in iris and the
# maximum values of that vector in iris2:
# iris => data.frame
iris[,num_cols] <- Map(function(i){
pmin(
iris[,i],
max(
iris2[,i],
na.rm = TRUE
)
)
},
num_cols
)
对于 tidyverse
方法,您可以使用 transmute
而不是 mutate
。 transmute
每次迭代中只会 return 一列,而 mutate
每次都会 return 所有列。
除此之外,为了让它更 tidyverse
友好,我使用 .data
而不是 get
。还使用 pmin
而不是复杂的 ifelse
解决方案。
library(dplyr)
library(purrr)
fixmax<- function(data,data2,var1,var2) {
data<- data %>% transmute("{var1}" := pmin(.data[[var1]], max(data2[[var2]])))
return(data)
}
要将该函数应用到每一对列,您可以使用 map2_dfc
,它还会将结果合并到一个数据框中。
lst1 <- names(iris[,1:4])
lst2 <- names(iris2[,1:4])
在应用函数之前比较两个数据帧的最大值。
map_dbl(iris[lst1], max)
#Sepal.Length Sepal.Width Petal.Length Petal.Width
# 9.9 6.4 8.9 4.5
map_dbl(iris2[lst2], max)
#Sepal_Length Sepal_Width Petal_Length Petal_Width
# 7.9 4.4 6.9 2.5
应用函数-
iris[lst1] <- map2_dfc(lst1, lst2, ~fixmax(iris, iris2, .x, .y))
应用函数后比较两个数据帧的最大值。
map_dbl(iris[lst1], max)
#Sepal.Length Sepal.Width Petal.Length Petal.Width
# 7.9 4.4 6.9 2.5
map_dbl(iris2[lst2], max)
#Sepal_Length Sepal_Width Petal_Length Petal_Width
# 7.9 4.4 6.9 2.5
您可以利用 R
中内置的矢量化,而不是按名称显式迭代不同的数据集和列。如果数据帧具有相同的 column/variable,则使用 mapply
或 purrr::map2
对映射到两个数据帧的函数进行排序将逐列迭代,而无需指定列名。
给定两个输入数据帧(df_small
和 df_big
),步骤是:
- 计算
df_small
中每列的最大值以创建df_small_max
- 将
pmin
函数应用到 df_big
的每一列和 df_small_max
的每个值,使用 mapply
(或者 purr::map2_dfc
如果你喜欢 tidyverse
映射)
#set up fake data
df_small <- iris[,1:4]
df_big <- df_small + 2
# find max of each col in df_small
df_small_max <- sapply(df_small, max)
# replace values of df_big which are larger than df_small_max
df_big_fixed <- mapply(pmin, df_big, df_small_max)
# sanity check -- Note the change in Sepal.Width
df_small_max
#> Sepal.Length Sepal.Width Petal.Length Petal.Width
#> 7.9 4.4 6.9 2.5
head(df_big, 3)
#> Sepal.Length Sepal.Width Petal.Length Petal.Width
#> 1 7.1 5.5 3.4 2.2
#> 2 6.9 5.0 3.4 2.2
#> 3 6.7 5.2 3.3 2.2
head(df_big_fixed, 3)
#> Sepal.Length Sepal.Width Petal.Length Petal.Width
#> [1,] 7.1 4.4 3.4 2.2
#> [2,] 6.9 4.4 3.4 2.2
#> [3,] 6.7 4.4 3.3 2.2
由 reprex package (v2.0.0)
于 2021-07-31 创建
这是一个基本的方法。我还重命名了变量,因为我在复制时遇到了一些麻烦,因为最初该方法会保存 iris
对象。
我们的方法是不改变 data.frame
对象,而是只 return 我们修改后的函数的预期值向量。然后,我们将这些值重新分配回我们原来的 data.frame
.
fixmax2 = function(x, y) {
max_y = max(y, na.rm = TRUE)
ifelse(x >= max_y, max_y, y)
}
cols = which(sapply(df_plus, is.numeric))
df_plus[cols] = Map(fixmax2, df_plus[cols], df_iris[cols])
df_plus
原始数据:
library(dplyr)
df_plus = iris %>% mutate_at((1:4), ~. + 2) ## let's not save over iris
df_iris = iris
names(df_iris)<-sub(".", "_", names(df_iris), fixed = TRUE)
您可以通过创建一个在每列中重复的最大值矩阵并使用 pmin
来获取 iris2 中的最大值和其他数据帧中的值之间的最小值来实现此目的。我创建了一个新的 fixmax 函数,它只将两个数据帧作为参数。
正在准备数据
library(tidyverse)
initial <- iris %>% mutate_at(1:4, ~.+2)
iris2 <- iris
names(iris2)<-sub(".", "_", names(iris2), fixed = TRUE)
print(max(initial$Sepal.Length))
# [1] 9.9
print(max(iris2$Sepal_Length))
# [1] 7.9
正在创建函数
fixmax <- function(df, dfmax){
colids <- which(unlist(lapply(dfmax, is.numeric)))
dfmax <- apply(dfmax[, colids], 2, max) %>%
matrix(nrow=nrow(dfmax), ncol=length(colids), byrow=TRUE) %>%
as.data.frame()
df[, colids] <- pmin(df[,colids], dfmax)
return(df)
}
测试功能
newiris <- fixmax(initial, iris2)
print(max(newiris$Sepal.Length))
# [1] 7.9
assertthat::assert_that(!identical(newiris, iris2))
# [1] TRUE
assertthat::assert_that(all((initial == newiris) || (iris2 == newiris)))
# [1] TRUE
imax = apply(iris2[, 1:4], 2, max) %>%
matrix(nrow=nrow(iris2), ncol=4, byrow=TRUE) %>%
as.data.frame()
assertthat::assert_that(all(newiris[, 1:4] <= imax))
# [1] TRUE
print(head(newiris))
# Sepal.Length Sepal.Width Petal.Length Petal.Width Species
# 1 7.1 4.4 3.4 2.2 setosa
# 2 6.9 4.4 3.4 2.2 setosa
# 3 6.7 4.4 3.3 2.2 setosa
# 4 6.6 4.4 3.5 2.2 setosa
# 5 7.0 4.4 3.4 2.2 setosa
# 6 7.4 4.4 3.7 2.4 setosa
我有两个看起来像这样的 df
library(tidyverse)
iris <- iris%>% mutate_at((1:4),~.+2)
iris2 <- iris
names(iris2)<-sub(".", "_", names(iris2), fixed = TRUE)
我的目标是减少 iris
中高于 iris2
中相应变量最大值的变量值,以匹配 iris2
中的最大值。
我已经编写了一个函数来执行此操作。
max(iris$Sepal.Length)
[1] 9.9
max(iris2$Sepal_Length)
[1] 7.9
# i want every value of iris that is >= to max value of iris2 to be equal to the max value of iris 2.
# my function:
fixmax<- function(data,data2,var1,var2) {
data<- data %>%
mutate("{var1}" := ifelse(get(var1)>=max(data2[[var2]],na.rm = T),
max(data2[[var2]],na.rm = T),get(var1)))
return(data)
}
# apply my function to a variable
tst_iris <- fixmax(iris,iris2,"Sepal.Length","Sepal_Length")
max(tst_iris$Sepal.Length)
7.9 # it works!
我面临的挑战是我想按顺序迭代我的函数超过两个变量列表 - 即Sepal.Length
Sepal_Length
、Sepal.Width
和Sepal_Width
等
有人知道我该怎么做吗?
我尝试使用 Map
但我做错了。
lst1 <- names(iris[,1:4])
lst2 <- names(iris2[,1:4])
final_iris<- Map(fixmax,iris, iris2,lst1,lst2)
我的目标是获得一个 df (final_iris
),其中每个变量都已使用 fixmax
指定的标准进行了调整。
我知道我可以通过 运行 在每个变量上设置我的函数来做到这一点。
final_iris <- iris
final_iris <- fixmax(final_iris,iris2,"Sepal.Length","Sepal_Length")
final_iris <- fixmax(final_iris,iris2,"Sepal.Width","Sepal_Width")
final_iris <- fixmax(final_iris,iris2,"Petal.Length","Petal_Length")
final_iris <- fixmax(final_iris,iris2,"Petal.Width","Petal_Width")
但在实际数据中,我必须 运行 这个操作数十次,我希望能够按顺序循环我的函数。
有谁知道我如何按顺序在 lst1
和 lst2
上循环我的 fixmax
?
这是您所期待的吗?
my_a <- iris %>% mutate_at((1:4),~.+2)
iris2 <- iris
names(iris2)<-sub(".", "_", names(iris2), fixed = TRUE)
my_var <- which(my_a$Sepal.Length >= max(iris2$Sepal_Length) & my_a$Sepal.Width >= max(iris2$Sepal_Width))
if (length(my_var)) {
my_a <- my_a[my_var,]
}
您的问题很可能与数据框本身就是列表这一事实有关。 Map()
期望非函数参数是相同长度的列表。任何比最长列表短的参数都会被“回收”以匹配它的长度。
目前,您有:
final_iris<- Map(fixmax,iris, iris2,lst1,lst2)
这实际上等同于:
final_iris<- Map(fixmax,
list(iris$Sepal.Length,
iris$Sepal.Width,
iris$Petal.Length,
iris$Petal.Width,
iris$Species),
list(iris2$Sepal_Length,
iris2$Sepal_Width,
iris2$Petal_Length,
iris2$Petal_Width,
iris2$Species),
lst1,
lst2)
我怀疑您希望将 iris
和 iris2
提供给对 fixmax()
的每次调用。为了让 Map()
像这样回收它们,它们需要是单元素列表。那就是你可能想要的:
final_iris<- Map(fixmax, list(iris), list(iris2),lst1,lst2)
要将数据帧列表合并为一个数据帧,请执行
do.call(rbind, final_iris)
你的函数乍一看似乎很复杂,难以阅读。我们可以使用快速函数
为列中的每个值整理函数 return max(x, max_val)#function to correct max
adjust_max <- function(x, max_val) {
return(ifelse(x >= max_val, max_val, x))
}
最后,我们希望使用这两个数据帧自动并按顺序应用它。我们将使用一个简单的 for 循环。附上设置问题的代码。
#libraries
library(tidyverse)
#set up fake data
iris_big <- iris%>% mutate_at((1:4),~.+2)
iris_small <- iris
names(iris_small)<- sub(".", "_", names(iris_small), fixed = TRUE)
#check which is the bigger one and the smaller
max(iris_big$Sepal.Length) #bigger
max(iris_small$Sepal_Length) #smaller
#function to correct max
adjust_max <- function(x, max_val) {
return(ifelse(x >= max_val, max_val, x))
}
#apply it to get a final result
iris_final <- iris_big
# iterate over columns, assuming same positions
# you can edit the 1:ncol(iris_final) to only take the columns you want
for (i in 1:ncol(iris_final)) {
#check numeric
if (is.numeric(iris_final[,i])) {
#applies the function - notice we call iris_final and iris_small
iris_final[,i] <- sapply(iris_final[,i],
adjust_max,
max_val = max(iris_small[,i]))
}
}
#check answer is correct
apply(iris_final[,1:4], 2, max)
apply(iris_small[,1:4], 2, max)
tail(iris_final)
您应该考虑使用列索引;一个完整的(不包括数据框架结构)基础 R 解决方案可能如下所示:
# Resolve the indices of the numeric vectors in
# iris: num_cols => integer vector
num_cols <- which(
vapply(
iris,
is.numeric,
logical(1)
),
arr.ind = TRUE
)
# Map the pmin function over iris to select the
# minimum of the vector element in iris and the
# maximum values of that vector in iris2:
# iris => data.frame
iris[,num_cols] <- Map(function(i){
pmin(
iris[,i],
max(
iris2[,i],
na.rm = TRUE
)
)
},
num_cols
)
对于 tidyverse
方法,您可以使用 transmute
而不是 mutate
。 transmute
每次迭代中只会 return 一列,而 mutate
每次都会 return 所有列。
除此之外,为了让它更 tidyverse
友好,我使用 .data
而不是 get
。还使用 pmin
而不是复杂的 ifelse
解决方案。
library(dplyr)
library(purrr)
fixmax<- function(data,data2,var1,var2) {
data<- data %>% transmute("{var1}" := pmin(.data[[var1]], max(data2[[var2]])))
return(data)
}
要将该函数应用到每一对列,您可以使用 map2_dfc
,它还会将结果合并到一个数据框中。
lst1 <- names(iris[,1:4])
lst2 <- names(iris2[,1:4])
在应用函数之前比较两个数据帧的最大值。
map_dbl(iris[lst1], max)
#Sepal.Length Sepal.Width Petal.Length Petal.Width
# 9.9 6.4 8.9 4.5
map_dbl(iris2[lst2], max)
#Sepal_Length Sepal_Width Petal_Length Petal_Width
# 7.9 4.4 6.9 2.5
应用函数-
iris[lst1] <- map2_dfc(lst1, lst2, ~fixmax(iris, iris2, .x, .y))
应用函数后比较两个数据帧的最大值。
map_dbl(iris[lst1], max)
#Sepal.Length Sepal.Width Petal.Length Petal.Width
# 7.9 4.4 6.9 2.5
map_dbl(iris2[lst2], max)
#Sepal_Length Sepal_Width Petal_Length Petal_Width
# 7.9 4.4 6.9 2.5
您可以利用 R
中内置的矢量化,而不是按名称显式迭代不同的数据集和列。如果数据帧具有相同的 column/variable,则使用 mapply
或 purrr::map2
对映射到两个数据帧的函数进行排序将逐列迭代,而无需指定列名。
给定两个输入数据帧(df_small
和 df_big
),步骤是:
- 计算
df_small
中每列的最大值以创建df_small_max
- 将
pmin
函数应用到df_big
的每一列和df_small_max
的每个值,使用mapply
(或者purr::map2_dfc
如果你喜欢tidyverse
映射)
#set up fake data
df_small <- iris[,1:4]
df_big <- df_small + 2
# find max of each col in df_small
df_small_max <- sapply(df_small, max)
# replace values of df_big which are larger than df_small_max
df_big_fixed <- mapply(pmin, df_big, df_small_max)
# sanity check -- Note the change in Sepal.Width
df_small_max
#> Sepal.Length Sepal.Width Petal.Length Petal.Width
#> 7.9 4.4 6.9 2.5
head(df_big, 3)
#> Sepal.Length Sepal.Width Petal.Length Petal.Width
#> 1 7.1 5.5 3.4 2.2
#> 2 6.9 5.0 3.4 2.2
#> 3 6.7 5.2 3.3 2.2
head(df_big_fixed, 3)
#> Sepal.Length Sepal.Width Petal.Length Petal.Width
#> [1,] 7.1 4.4 3.4 2.2
#> [2,] 6.9 4.4 3.4 2.2
#> [3,] 6.7 4.4 3.3 2.2
由 reprex package (v2.0.0)
于 2021-07-31 创建这是一个基本的方法。我还重命名了变量,因为我在复制时遇到了一些麻烦,因为最初该方法会保存 iris
对象。
我们的方法是不改变 data.frame
对象,而是只 return 我们修改后的函数的预期值向量。然后,我们将这些值重新分配回我们原来的 data.frame
.
fixmax2 = function(x, y) {
max_y = max(y, na.rm = TRUE)
ifelse(x >= max_y, max_y, y)
}
cols = which(sapply(df_plus, is.numeric))
df_plus[cols] = Map(fixmax2, df_plus[cols], df_iris[cols])
df_plus
原始数据:
library(dplyr)
df_plus = iris %>% mutate_at((1:4), ~. + 2) ## let's not save over iris
df_iris = iris
names(df_iris)<-sub(".", "_", names(df_iris), fixed = TRUE)
您可以通过创建一个在每列中重复的最大值矩阵并使用 pmin
来获取 iris2 中的最大值和其他数据帧中的值之间的最小值来实现此目的。我创建了一个新的 fixmax 函数,它只将两个数据帧作为参数。
正在准备数据
library(tidyverse)
initial <- iris %>% mutate_at(1:4, ~.+2)
iris2 <- iris
names(iris2)<-sub(".", "_", names(iris2), fixed = TRUE)
print(max(initial$Sepal.Length))
# [1] 9.9
print(max(iris2$Sepal_Length))
# [1] 7.9
正在创建函数
fixmax <- function(df, dfmax){
colids <- which(unlist(lapply(dfmax, is.numeric)))
dfmax <- apply(dfmax[, colids], 2, max) %>%
matrix(nrow=nrow(dfmax), ncol=length(colids), byrow=TRUE) %>%
as.data.frame()
df[, colids] <- pmin(df[,colids], dfmax)
return(df)
}
测试功能
newiris <- fixmax(initial, iris2)
print(max(newiris$Sepal.Length))
# [1] 7.9
assertthat::assert_that(!identical(newiris, iris2))
# [1] TRUE
assertthat::assert_that(all((initial == newiris) || (iris2 == newiris)))
# [1] TRUE
imax = apply(iris2[, 1:4], 2, max) %>%
matrix(nrow=nrow(iris2), ncol=4, byrow=TRUE) %>%
as.data.frame()
assertthat::assert_that(all(newiris[, 1:4] <= imax))
# [1] TRUE
print(head(newiris))
# Sepal.Length Sepal.Width Petal.Length Petal.Width Species
# 1 7.1 4.4 3.4 2.2 setosa
# 2 6.9 4.4 3.4 2.2 setosa
# 3 6.7 4.4 3.3 2.2 setosa
# 4 6.6 4.4 3.5 2.2 setosa
# 5 7.0 4.4 3.4 2.2 setosa
# 6 7.4 4.4 3.7 2.4 setosa