【问题标题】:R - using a for loop to modify a column in a data frame (and using factor levels)R - 使用 for 循环修改数据框中的列(并使用因子级别)
【发布时间】:2014-06-11 03:53:20
【问题描述】:

我正在尝试将日期因子转换为可由 for 循环引用的字符向量。 for 循环应将数据框的“Day”列(例如,如下所示)中的 NA 值替换为与日期对应的值。

     Date    Time Axis1 Day Sum.A1.Daily
1 6/12/10 5:00:00    20  NA           NA
2 6/12/10 5:01:00    40  NA           NA
3 6/13/10 5:02:00    50  NA           NA
4 6/13/10 5:03:00    10  NA           NA
5 6/14/10 5:04:00    20  NA           NA
6 6/14/10 5:05:00    30  NA           NA

我需要把它改成这样:

     Date    Time Axis1 Day Sum.A1.Daily
1 6/12/10 5:00:00    20   1           60
2 6/12/10 5:01:00    40   1           60
3 6/13/10 5:02:00    50   2           80
4 6/13/10 5:03:00    30   2           80
5 6/14/10 5:04:00    20   3           50
6 6/14/10 5:05:00    30   3           50

使用我当前的代码,我得到的是:

     Date    Time Axis1 Day Sum.A1.Daily
1 6/12/10 5:00:00    20  NA           60
2 6/12/10 5:01:00    40  NA           60
3 6/13/10 5:02:00    50  NA           80
4 6/13/10 5:03:00    30  NA           80
5 6/14/10 5:04:00    20  NA           50
6 6/14/10 5:05:00    30  NA           50

将值分配给第 4 列的 for 循环出现问题。我需要帮助理解两件事:

  1. 问题是什么(下面的当前脚本)
  2. 如果我可以通过更有效地使用因子水平来规避问题

我是 R 和 stackoverflow 的新手——对这个社区的酷炫感到不知所措。如果我违反了基本的提问规则,请告诉我。

## read in file; define classes 
## (important b/c I want R to utilize factor levels of "Date" in column 1 of .csv file)
dat <- read.csv("data.csv", header = T, ## read in file
      colClasses = c("factor", "character", "integer", "integer", "integer"))

## assign values to be used by for loops
levs <- lapply(dat, levels) ## grab levels for factor variable of dates
dates <- c(levs$Date) ## creates list of dates to reference in for loop
counts <- c(1:length(dates)) ## creates vector 1:number of dates listed in file for loop 2
x <- (1:nrow(dat)) ## creates vector 1:number of rows in file

## for loop 1 will cycle through rows in file; 
## for loop 2 cycle through values in "counts" variable
      ## if() compares value of each object in "Dates" (col. 1) 
       ## to one of the value of one of the levels (e.g., compared to "6/22/10", not 1)
            ## if ==, assigns corresp. value of "counts" to the appropriate obs. of col. 4 

("Day")
    for (i in x) {
          for (j in counts) {
                if (dat[i,1] == levs[j]) {
                      dat[i,4] <- counts[j]
                }
          }
    }
dat <- transform(dat, Sum.A1.Daily = ave(dat$Axis1, dat$Date, FUN = sum))
if(!file.exists("ActData.csv")) {     ## Enter file name for new data
write.csv(dat, file = "ActData2.csv") ## Enter file name for new data
  } else { stop("change file name") 
}
print("File Cleaning Complete")
head(dat)
tail(dat)

【问题讨论】:

  • ?cat - (Returned) Value: None。不要尝试将没有值的cat(...) 分配给对象。
  • 那我怎样才能将这组新的值赋给一个对象呢?
  • 从我猜测的代码中删除cat()
  • 为什么要加逗号?
  • 因为这是我知道我的嵌套 for 循环(如当前编写的)将接受数据的唯一方法。我是一个极端的新手,我试图一次操纵一件事。

标签: r date for-loop vector r-factor


【解决方案1】:

这是一种循环效率非常低的问题。尝试使用矢量化方法:

dat$day <- as.numeric(factor(dat$Date))  
dat$Sum.A1.Daily <- ave(dat$Axis1, dat$Date, FUN=sum)

第一个使用因子实际上是 alpha 水平向量的整数索引这一事实。在这种情况下,我们只是丢弃了levels属性,只使用了整数系列。

编辑:等等!;您已经在 transform 中正确使用了它:ave 在第二个参数的类别中计算 FUN 参数的值,并返回一个与其第一个参数长度相同的向量。

【讨论】:

  • 这是一个很好的答案。非常感谢。结果,我觉得我对循环的功能有了更好的理解。从现在开始,我将在我的代码中更加谨慎地使用它们。
【解决方案2】:

您可以使用match 获取“日”列的值。然后splitsapply,获取“Sum.A1.Daily”列的值。假设你的原始数据是dat

> within(dat, {
      Day <- match(Date, levels(Date))
      Sum.A1.Daily <- sapply(split(Axis1, Day), sum)[Day]
  })
#      Date    Time Axis1 Day Sum.A1.Daily
# 1 6/12/10 5:00:00    20   1           60
# 2 6/12/10 5:01:00    40   1           60
# 3 6/13/10 5:02:00    50   2           80
# 4 6/13/10 5:03:00    30   2           80
# 5 6/14/10 5:04:00    20   3           50
# 6 6/14/10 5:05:00    30   3           50

为了分解这些部分,让我们分别看一下它们。首先,在列上使用 match 以及列的因子级别会返回一个数值向量,其中包含列中属于每个级别的值的索引。

> (m <- with(dat, match(Date, levels(Date))))
# [1] 1 1 2 2 3 3

然后,将“Axis1”列除以“Date”列并对其进行迭代以获得总和,用[m] 向量化,我们得到以下结果。

> with(dat, sapply(split(Axis1, Date), sum)[m])
# 6/12/10 6/12/10 6/13/10 6/13/10 6/14/10 6/14/10 
#      60      60      80      80      50      50 

within()允许我们对数据框执行操作并一次调用返回结果。


现在,就您的代码而言,我将对您使用transform的位置进行以下更改

dates <- lapply(dat, levels)$Date 
  ## grab levels for factor variable of dates
counts <- match(dat$Date, levels(dat$Date)) 
  ## creates vector 1:number of dates listed in file for loop 2
for(i in seq(dates)){
    for(j in seq(counts)){
        if(dat$Date[j] %in% dates) dat$Day[j] <- counts[j]
    }
}

【讨论】:

  • 感谢理查德 - 在一分钟内重新发布更好的代码/问题
猜你喜欢
  • 2021-06-26
  • 1970-01-01
  • 1970-01-01
  • 2019-08-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-31
相关资源
最近更新 更多