【问题标题】:R - Trying to avoid a loop hereR - 试图在这里避免循环
【发布时间】:2017-03-28 13:05:37
【问题描述】:

第一次在这里发帖,但我已经阅读了很多,所以谢谢!

我有一个包含许多列的巨大数据框,但这里只有 4 个重要:

dates/classes/names/grades.

对于每个日期,我有几个班级(有学生),每个班级都有几个人(名字 - 在各自班级中总是相同的人),每个班级每个日期都有一个成绩。

在第一次约会时,我使用max[] 检索每个班级考虑到他的成绩最好的学生。
但是,对于接下来的日期,我想做以下事情:

  • 如果之前最好的学生仍然在班级前 3 名,那么我们认为他仍然是最好的。
  • 否则,我们认为新的第一名学生是最​​好的。

因此,每个日期都取决于前一个日期。

是否可以在没有循环的情况下执行此操作?
我不知道如何,因为每次迭代都取决于前一次。

这是我下面的代码。 未优化请见谅!

非常感谢:)

for (i in (1:(length(horizon)-1))) #horizon is the vector of dates
{
    uni3 <- dataaf[dataaf[,1] == as.numeric(horizon[i]),]     #dataaf contains all the data, we only keep the date for the considered date i

    if (i == 1)                             #we take the best student per class
    {
        selecdate <- data.frame()                             #selecdate is the dataframe containing the best people for this date

        for (z in (1:15)    #15 classes
        {
            selecsec <- na.omit(uni3[uni3[,14] == z,])                 #classes are column 14
            ligneselec <- max(selecsec[,13])                          #grades are column 13
            selecsec <- data.frame(uni3[match(ligneselec,uni3[,13]),])
            selecdate <- rbind(selecdate,selecsec)
        }
    } 
    else {              #we keep a student if he was in the previous top 3, else we take the best one
        selecdate <- data.frame()

        for (z in (1:15))
        {
            lastsec <- na.omit(lastdate[lastdate[,14] == z,])         #last results

            #retrieving the top 3 people this date
            selecsec <- na.omit(uni3[uni3[,14] == z,])
            newligneselec <- tail(sort(selecsec[,13]),3)
            selecsec <- data.frame(selecsec[rev(match(newligneselec,selecsec[,13])),])

            if((length(match(selecsec[,3],lastsec[,3])[!is.na(match(selecsec[,3],lastsec[,3]))]) == 0)) 
            {
                ligneselec <- max(selecsec[,13])
                selecsec <- data.frame(uni3[match(ligneselec,uni3[,13]),])
            } 
            else 
            {
                selecsec <- lastsec
            } 

            selecdate <- rbind(selecdate,selecsec)
        }
    }

    lastdate <- selecdate #recording the last results
}

编辑:这是一个例子。

  • 在日期 1 中,约翰和奥黛丽都被选入了 1 和 2 类。
  • 在第 2 天,约翰仍然是最好的 3 人之一,所以他仍然被选中, 而奥黛丽只有第四名,所以吉姆(在日期 2 中排名第一)取代 她。
  • 在第 3 天,约翰仍然是最好的 3 人之一,因此他仍然被选中(我处理的数据中没有平局问题)。 Jim 现在是第 4 位,所以 Sandra 取代了他的位置。

    结构(列表(日期=结构(c(1L,1L,1L,1L,1L,1L,1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L, 3L), .Label = c("12/02", "13/02", "14/02" ), class= "因子"), 类 = c(1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2 ), 名称 = 结构 (c(6L, 3L, 9L, 7L, 1L, 8L, 4L, 10L, 5L, 2L, 6L, 3L, 9L, 7L, 1L, 8L, 4L, 10L, 5L, 2L, 6L, 3L, 9L, 7L, 1L, 8L, 4L, 10L, 5L, 2L), .Label = c("Ashley", "Audrey", "Bob", "Denis", “吉姆”,“约翰”,“金”,“桑德拉”,“特里”,“蒂姆”),class=“因素”), 等级 = c(10, 5, 3, 2, 1, 3, 4, 5, 6, 7, 8, 2, 10, 9, 1, 7, 5, 1, 8, 2, 5, 1, 4, 8, 8, 7, 6, 5, 4, 3)), .Names = c("日期", "Classes", "Names", "Grades"), row.names = c(NA, -30L), class= "data.frame")

【问题讨论】:

  • 请提供一个最小的例子。
  • 我无法插入表格...抱歉
  • 在您的data.frame 上使用dput 获取示例,请参阅here
  • 你想如何处理领带?
  • 平局不会是问题,我在这里举了一个简单的例子,但不太可能有平局! @count 谢谢,我马上去查一下 :)

标签: r loops


【解决方案1】:

已编辑以反映 cmets 中的明确要求。

###---------- CREATING THE DATA (may be different from what you had in mind)
# Classes and Students
Classes <- c("U.S. History", "English", "NonLinear Optimization")
Students <- c("James", "Jamie", "John", "Jim", "Jane", "Jordan", "Jose")
df.1 <- expand.grid(Classes = Classes, Students = Students, stringsAsFactors = T)
# Generate Dates
Dates.seq <- seq(as.Date("2017/2/10"), as.Date("2017/3/27"), "days")
df.2 <- merge(Dates.seq, df.1)
# Generate Grades
grading <- c(4.0, 3.7, 3.3, 3.0, 2.7, 2.3, 2.0, 1.7)
Grades <- sample(grading, size = dim(df.2)[1], replace = T, prob = grading/sum(grading)) # smart students
df <- data.frame(df.2, Grades)
colnames(df) <- c("Dates","Classes","Students","Grades")

# Works assuming your df has the following labeled and formatted columns
str(df)
#'data.frame':  966 obs. of  4 variables:
#  $ Dates   : Date, format: "2017-02-10" "2017-02-11" "2017-02-12" ...
#  $ Classes : Factor w/ 3 levels "U.S. History",..: 1 1 1 1 1 1 1 1 1 1 ...
#  $ Students: Factor w/ 7 levels "James","Jamie",..: 1 1 1 1 1 1 1 1 1 1 ...
#  $ Grades  : num  2.3 3.3 2.3 3.3 2.7 4 4 1.7 2.3 4 ...

# No aggregateion, just splitting by classes
df.split1 <- split(df, df[,"Classes"])
# Then splitting each of those lists by Dates
df.split2 <- lapply(df.split1, function(x) split(x, x[,"Dates"]))
# double the lapply becuase now we have lists within lists
top1 <- lapply(df.split2, function(i) lapply(i, function(j) j[order(-j[,"Grades"])[1], "Students"]))
top3 <- lapply(df.split2, function(i) lapply(i, function(j) j[order(-j[,"Grades"])[1:3], "Students"]))

# Easier to read
AllClasses <- levels(df[,"Classes"])
AllDates <- unique(df[,"Dates"])

# Initialize a matrix to keep track of changes in the Top1 and Top3
superstar <- matrix(NA, nrow = length(AllDates), ncol = length(AllClasses), 
                    dimnames = list(as.character(AllDates), AllClasses))

# Looping
for(date in 1:length(AllDates)){
  for(class in AllClasses){
    if(date == 1){ 
      # First NewTop1 = First Top1 
      superstar[date, class] <- unlist(top1[[class]][date])
    } else {
      # If superstar in date-1 is in the Top3 of date now,
      if(superstar[date-1, class] %in% as.numeric(unlist(top3[[class]][date]))){
        # still superstar
        superstar[date,class] <- superstar[date-1, class]
      } else{
        # new superstar is highest scorer of date now
        superstar[date,class] <- unlist(top1[[class]][date])
      }
    }
  }
}
# painful for me trying to figure out how to convert superstar numbers to names but this worked
superstar.char <- as.data.frame(matrix(levels(df[,"Students"])[superstar], ncol = length(AllClasses)))
dimnames(superstar.char) <- dimnames(superstar)
superstar.char # superstar with Students as characters 

如果您有任何困难,请告诉我!

【讨论】:

  • 您好,非常感谢!我一直在尝试使用您的代码,这看起来很棒。不幸的是,我不想汇总班级(我希望每个班级最好的学生),而且我在调整其余代码时遇到了一些麻烦。
  • 您好!我想我对我到底想要什么给出了错误的解释。日期 i 中的超级巨星如果在前 3 名中仍然是日期 (i+1) 中的超级巨星,但如果他在日期前 3 中 (i+2) 中,他仍然是超级巨星。您的代码似乎没有这样做。请参阅下面的第 8 行,我原本预计 Jose 会继续成为超级巨星(第 1 列是前 1 列,第 2-4 列是前 3 列,第 5 列是超级巨星)。
  • 结构(c(“詹姆斯”,“吉姆”,“乔丹”,“约翰”,“乔丹”,“何塞”,“简”,“詹姆斯”,“詹姆斯”,“吉姆”,“乔丹”,“约翰”,“乔丹”,“何塞”,“简”,“詹姆斯”,“乔丹”,“约翰”,“吉姆”,“何塞”,“简”,“简”, “乔丹”、“何塞”、“吉姆”、“詹姆斯”、“简”、“杰米”、“何塞”、“詹姆斯”、“何塞”、“杰米”、“詹姆斯”、“詹姆斯”、“吉姆” ", "约翰", "乔丹", "何塞", "何塞", "詹姆斯"), .Dim = c(8L, 5L), .Dimnames = list( NULL, c("", "", "" , "", "超级巨星")))
  • 只有在 [i+0] 的前 3 名中,他才是 [i+1] 的超级明星。然后在 [i+2] 中,如果他实际上在前 3 名中最高并且也在 [i+1] 中,让他成为超级巨星 - 知道了,我会再试一次。这是跨日期的每个班级,所以如果每个学生参加 3 节课,每个日期会有 3 位超级明星?每个班级1个?
  • 您好,感谢您的工作,非常感谢!我会花时间去适应它。看起来没有办法避免这个循环? :)
【解决方案2】:

可以使用递归函数(一个调用自身的函数)来解决您在循环中解决的任何问题。由于您要根据 i 更改函数的行为,因此您需要将 i 作为参数传递给函数。您还需要该函数能够实现何时完成并返回结果集。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-08-09
    • 2012-08-20
    • 2020-06-29
    • 1970-01-01
    • 2011-03-20
    相关资源
    最近更新 更多