试图避免r中的for循环

时间:2018-04-23 23:52:38

标签: r

我有一些有效的代码,但非常笨重,我确信有更好的方法可以做到这一点,避免使用for循环。基本上我有一份表演清单和一系列因素。我希望将最高性能分配给最高因素,将最低性能分配给最低因素等。以下是一些简化的示例代码:

#My simplified sample list of performances:

PerformanceList <- data.frame(v1 <- c(rep(10,4)), v2 <- c(rep(9,4)), v3 <- c(rep(8,4)))


View(PerformanceList)
v1                  v2                v3
1  10                  9                  8
2  10                  9                  8
3  10                  9                  8
4  10                  9                  8


#My simplified sample list of Factors:

MyFactors <- data.frame(v1 <- c(35,25,15,5), v2 <- c(10,20,60,20), v3 <- c(5,10,15,40))

View(MyFactors)
     v1                  v2                 v3
1    35                  10                  5
2    25                  20                  10
3    15                  60                  50
4    5                   20                  40


#Code to find the ranking of each row from largest to smallest:

Rankings <- data.frame(t(apply(-MyFactors, 1, rank, na.last="keep",ties.method="random")))

View(Rankings)
          v1          v2         v3
1         1           2           3
2         1           2           3
3         3           1           2
4         3           2           1

按排名对每一行进行排序的功能。我认为有更好的方法可以做到这一点,但我无法弄清楚:

 SortFunction <- function(RankingList){
 SortedRankings <- order(RankingList)
 return(SortedRankings)
 }

#applying that Sort function to each row of the data frame: 

SortedRankings <- data.frame(t(apply(Rankings, 1,SortFunction)))

View(SortedRankings)
    X1   X2   X3
1   1    2    3
2   1    2    3
3   2    3    1
4   3    2    1

这是一个执行我想要的for循环,但我确定它不是最好的方法。基本上我想沿着我的PerformanceList的每一行,选择与最高排名相对应的列(从上面的排序排名第1列​​)。我非常希望能够从这些排序排名中分配第2列,将第二高的性能分配给我的第二高因素,依此类推......

FactorPerformanceList <- data.frame(matrix(NA, ncol=1, nrow=NROW(Rankings)))
for (i in 1:NROW(Rankings)){
FactorPerformanceList[i,] <- PerformanceList[i,SortedRankings[i,1]]
}

View(FactorPerformanceList)
1     10
2     10
3      9
4      8

看起来这应该可行,但它提供了一个4行乘4列的矩阵:

FactorPerformanceList2 <- PerformanceList[,SortedRankings[,1]]

View(FactorPerformanceList2)
   v1     v1     v2      v3 
1  10     10      9       8
2  10     10      9       8
3  10     10      9       8
4  10     10      9       8

任何想法或帮助将不胜感激!谢谢!

2 个答案:

答案 0 :(得分:0)

鉴于缺乏明确性,我已经为您提出了一些灵活的答案。 获取给定的data.frame并强制它采用长格式可能是有意义的,我们可以确保我们维护先前结构的索引位置,因为这可能是您可以用来将其他data.frames彼此连接起来的。

我已选择使用tidyverse套件来回答这个问题,即dplyr

数据

library(tidyverse)
PerformanceList <- data.frame(v1 = c(rep(10,4)), v2 = c(rep(9,4)), v3 = c(rep(8,4)))
MyFactors <- data.frame(v1 = c(35,25,15,5), v2 = c(10,20,60,20), v3 = c(5,10,15,40))

此函数将获取data.frame并提供具有索引位置列的长格式data.frame。

转换为索引等级为

的long data.frame的函数
df_ranks <- function(df) {

  names(df) <- 1:ncol(df)
  df %>%
    mutate(row_index = 1:nrow(.)) %>%
    gather(col_index, value, -row_index) %>%
    group_by(row_index) %>%
    mutate(row_rank = rank(value, na.last = "keep", ties.method = "random")) %>%
    group_by(col_index) %>%
    mutate(col_rank = rank(value, na.last = "keep", ties.method = "random")) %>%
    ungroup()

}

将该功能应用于数据,并确保调整列名称将让我们毫不费力地加入。

ranked_perf <- df_ranks(PerformanceList) %>% setNames(paste0("rank_", names(.)))
ranked_fact <- df_ranks(MyFactors) %>% setNames(paste0("fact_", names(.)))

然后我们可以加入表格,了解您想要做什么以及在此步骤之前预期结果可能很重要。对于这个例子,我已经说过我希望列中的匹配值按其等级排列。

full_join(ranked_perf, ranked_fact,
          by = c("rank_col_rank" = "fact_col_rank",
                 "rank_col_index" = "fact_col_index"))

至于您想要对此结果做什么,您可以选择列并使用selectunitespread的组合将其操作回宽屏格式

答案 1 :(得分:0)

这在技术上不会删除 for循环,它只是隐藏它。也就是说,它比你拥有的代码更清晰,除非你需要所有的中间数据步骤,否则它会大大简化。

PerformanceList <- data.frame(
  v1= c(rep(10,4)), 
  v2= c(rep(9,4)), 
  v3 = c(rep(8,4))
  )
MyFactors <- data.frame(
  v1 = c(35,25,15,5),
  v2 = c(10,20,60,20), 
  v3 = c(5,10,15,40))


FactorPerformanceList <- as.data.frame(t(sapply(1:nrow(PerformanceList), function(i) {
  PerformanceList[i,order(MyFactors[i,])]
})))

可以编写相同的代码

library(tidyverse)
FactorPerformanceList <- 1:nrow(PerformanceList) %>%
sapply(function(i) {
  PerformanceList[i,order(MyFactors[i,])]
}) %>%
t() %>%
as.data.frame()

使操作顺序更加清晰(sapply,然后是t,然后是as.data.frame)。

通常,当您使用列时,可以完全避免for循环,但是逐行操作并不容易完全删除。您可以使用apply系列函数来清理代码,或者(如果您想要更高级的东西)plyrpurrr包。