在R中将多行转换为单列

时间:2015-03-18 18:57:52

标签: r dplyr melt tidyr

使用R,我有一个大数据框,下面是一个例子:

df = data.frame(X1 = c("02JAN2008","09:30 - 10:00", "10:00 - 10:30", "10:30 - 11:00","11:00 - 11:30", "15:30 - 16:00", "16:00 - 16:30", "03JAN2008",  "09:30 - 10:00", "10:00 - 10:30", "10:30 - 11:00", "11:00 - 11:30"),X2 = c(NA, 1469.37, 1459.91, 1456.92, 1453.48, 1447.22, 1447.16,NA, 1449.78, 1451.21, 1450.08, 1452.16),X3 = c(NA, 1467.97, 1467.11, 1459.76, 1457.00, 1444.00, 1447.67,NA, 1447.55, 1450.66, 1452.06, 1450.01))

看起来像:

              X1      X2      X3
1      02JAN2008      NA      NA
2  09:30 - 10:00 1469.37 1467.97
3  10:00 - 10:30 1459.91 1467.11
4  10:30 - 11:00 1456.92 1459.76
5  11:00 - 11:30 1453.48 1457.00
6  15:30 - 16:00 1447.22 1444.00
7  16:00 - 16:30 1447.16 1447.67
8      03JAN2008      NA      NA
9  09:30 - 10:00 1449.78 1447.55
10 10:00 - 10:30 1451.21 1450.66
11 10:30 - 11:00 1450.08 1452.06
12 11:00 - 11:30 1452.16 1450.01

由于缺少数据,有些日子可能会有6次观察,但对于其他日子,可能只有4次(或更少,这是一个例子)。

我想将其转换为数据框,每30分钟间隔将日期作为单独的列,例如:

          X1            X2      X3      X4
1  02JAN2008 09:30 - 10:00 1469.37 1467.97
2  02JAN2008 10:00 - 10:30 1459.91 1467.11
3  02JAN2008 10:30 - 11:00 1456.92 1459.76
4  02JAN2008 11:00 - 11:30 1453.48 1457.00
5  02JAN2008 15:30 - 16:00 1447.22 1444.00
6  02JAN2008 16:00 - 16:30 1447.16 1447.67
7  03JAN2008 09:30 - 10:00 1449.78 1447.55
8  03JAN2008 10:00 - 10:30 1451.21 1450.66
9  03JAN2008 10:30 - 11:00 1450.08 1452.06
10 03JAN2008 11:00 - 11:30 1452.16 1450.01

我可以轻松地抓取df的索引,其中X2是NA,然后写一个for循环来结转日期,但我想避免使用{{1}在R中循环。

我怎样才能在R中这样做?当然可以使用fordplyr解决方案,但我无法通过文档中的示例生成一个解决方案。或者也许某个版本的tidyr

5 个答案:

答案 0 :(得分:4)

这是一个选项:

library(data.table)
dt = as.data.table(df) # or setDT to convert in place

dt[, grp := cumsum(is.na(X2))][, c(date = list(X1[1]), tail(.SD, -1)), by = grp]
#    grp      date            X1      X2      X3
# 1:   1 02JAN2008 09:30 - 10:00 1469.37 1467.97
# 2:   1 02JAN2008 10:00 - 10:30 1459.91 1467.11
# 3:   1 02JAN2008 10:30 - 11:00 1456.92 1459.76
# 4:   1 02JAN2008 11:00 - 11:30 1453.48 1457.00
# 5:   1 02JAN2008 15:30 - 16:00 1447.22 1444.00
# 6:   1 02JAN2008 16:00 - 16:30 1447.16 1447.67
# 7:   2 03JAN2008 09:30 - 10:00 1449.78 1447.55
# 8:   2 03JAN2008 10:00 - 10:30 1451.21 1450.66
# 9:   2 03JAN2008 10:30 - 11:00 1450.08 1452.06
#10:   2 03JAN2008 11:00 - 11:30 1452.16 1450.01

答案 1 :(得分:4)

这是 dplyr 方式:

breaks <- is.na(df$X2)
df %>%
    mutate(date=X1[breaks][cumsum(breaks)]) %>%
    filter(!breaks)

#               X1   X2   X3      date
# 1  09:30 - 10:00 1469 1468 02JAN2008
# 2  10:00 - 10:30 1460 1467 02JAN2008
# 3  10:30 - 11:00 1457 1460 02JAN2008
# 4  11:00 - 11:30 1453 1457 02JAN2008
# 5  15:30 - 16:00 1447 1444 02JAN2008
# 6  16:00 - 16:30 1447 1448 02JAN2008
# 7  09:30 - 10:00 1450 1448 03JAN2008
# 8  10:00 - 10:30 1451 1451 03JAN2008
# 9  10:30 - 11:00 1450 1452 03JAN2008
# 10 11:00 - 11:30 1452 1450 03JAN2008

或者只是简单地在基础R:

df <- within(df, date <- X1[breaks][cumsum(breaks)])
df[! breaks, ]

答案 2 :(得分:3)

一种方法是na.locf来自zoo

require(zoo)
df0<-cbind(df$X1,df)
df0[!is.na(df0[,3]),1]<-NA
df0[,1]<-na.locf(df0[,1])
df0<-df0[!is.na(df0[,3]),]

给出了:

> df0    
       df$X1            X1      X2      X3
2  02JAN2008 09:30 - 10:00 1469.37 1467.97
3  02JAN2008 10:00 - 10:30 1459.91 1467.11
4  02JAN2008 10:30 - 11:00 1456.92 1459.76
5  02JAN2008 11:00 - 11:30 1453.48 1457.00
6  02JAN2008 15:30 - 16:00 1447.22 1444.00
7  02JAN2008 16:00 - 16:30 1447.16 1447.67
9  03JAN2008 09:30 - 10:00 1449.78 1447.55
10 03JAN2008 10:00 - 10:30 1451.21 1450.66
11 03JAN2008 10:30 - 11:00 1450.08 1452.06
12 03JAN2008 11:00 - 11:30 1452.16 1450.01

答案 3 :(得分:2)

base R选项

df$X1 <- as.character(df$X1)
indx <- !grepl(':', df$X1)
res <- setNames(data.frame(unlist(tapply(df$X1[indx][cumsum(indx)], 
          cumsum(indx), FUN=head, -1)), df[!indx,]), paste0("X",1:4))
row.names(res) <- NULL
res
#          X1            X2      X3      X4
#1  02JAN2008 09:30 - 10:00 1469.37 1467.97
#2  02JAN2008 10:00 - 10:30 1459.91 1467.11
#3  02JAN2008 10:30 - 11:00 1456.92 1459.76
#4  02JAN2008 11:00 - 11:30 1453.48 1457.00
#5  02JAN2008 15:30 - 16:00 1447.22 1444.00
#6  02JAN2008 16:00 - 16:30 1447.16 1447.67
#7  03JAN2008 09:30 - 10:00 1449.78 1447.55
#8  03JAN2008 10:00 - 10:30 1451.21 1450.66
#9  03JAN2008 10:30 - 11:00 1450.08 1452.06
#10 03JAN2008 11:00 - 11:30 1452.16 1450.01

或者

res2 <- do.call(rbind,lapply(Map(cbind, df$X1[indx],split(df[!indx,], 
           cumsum(indx)[!indx])), setNames, paste0('X', 1:4)))
row.names(res2) <- NULL

答案 4 :(得分:0)

我试过这个:

> na_ind <- which(is.na(df$X2))
> day_break <- c(na_ind, nrow(df) + 1)
> day_count <- day_break[-1] - day_break[-length(day_break)] -1
> day_count
## [1] 6 4
> new_df <- cbind(date = rep(df$X1[na_ind], times = day_count),
+                 df[-na_ind,])
> new_df
## date            X1      X2      X3
## 2  02JAN2008 09:30 - 10:00 1469.37 1467.97
## 3  02JAN2008 10:00 - 10:30 1459.91 1467.11
## 4  02JAN2008 10:30 - 11:00 1456.92 1459.76
## 5  02JAN2008 11:00 - 11:30 1453.48 1457.00
## 6  02JAN2008 15:30 - 16:00 1447.22 1444.00
## 7  02JAN2008 16:00 - 16:30 1447.16 1447.67
## 9  03JAN2008 09:30 - 10:00 1449.78 1447.55
## 10 03JAN2008 10:00 - 10:30 1451.21 1450.66
## 11 03JAN2008 10:30 - 11:00 1450.08 1452.06
## 12 03JAN2008 11:00 - 11:30 1452.16 1450.01
相关问题