2

给定以下数据框:

df <- data.frame(start = c("005", "010", "014"),
                   end = c("005", "013", "017"),
                  zone = c(3, 5, 7))
# df
#   start end zone
# 1   005 005    3
# 2   010 013    5
# 3   014 017    7

我想生成以下结果:

#   key zone
# 1 005    3
# 2 010    5
# 3 011    5
# 4 012    5
# 5 013    5
# 6 014    7
# 7 015    7
# 8 016    7
# 9 017    7

我在想我也许可以利用tidyr- 也许complete()或的东西expand(),但是有三个字符的字符串,df$start并且df$end一直给我带来麻烦。

我使用: 取得了一定的成功apply(df, 1, function(i) seq(as.numeric(i["start"]), as.numeric(i["end"]))),然后我可以将其传递给类似的东西stringr::str_pad(..., width = 3, pad = "0"),但我不确定如何巧妙地抓住这些重复序列的区域。

4

5 回答 5

5

使用data.table您可以通过链接在一起的三个步骤来完成此操作。首先,您将数据框转换为带有setDT. 其次,将startend列转换为数字。第三,您key通过为原始数据框中的每一行制作序列来创建列,其中列中的值start作为起始值,end列中的值作为结束值。第四,您将key列转换回字符,方法sprintf是在小于 3 位的数字前使用并添加零:

library(data.table)
cols <- names(df)[1:2]
setDT(df)[, (cols) := lapply(.SD, function(x) as.numeric(as.character(x))), .SDcols = cols
          ][, .(key = start:end), by = zone
            ][, key := sprintf('%03d',key)][]

这使:

   zone key
1:    3 005
2:    5 010
3:    5 011
4:    5 012
5:    5 013
6:    7 014
7:    7 015
8:    7 016
9:    7 017

根据您的startend列是否是因子的特征,您应该使用as.numeric(x)as.numeric(as.character(x))

在@alistaire 的评论之后,您可以奇怪地省略该as.numeric部分。因此:

setDT(df)[, (cols) := lapply(.SD, function(x) as.character(x)), .SDcols = cols
          ][, .(key = start:end), by = zone
            ][, key := sprintf('%03d',key)][]

会给你相同的结果(当你的startend列已经是字符类时,你当然可以省略这lapply一步)。

于 2016-06-15T16:30:44.783 回答
3

这是另一种基本 R 的可能性...

## Create the pairwise sequences after coercing factor columns to integer
x <- with(
    lapply(df[-3], function(x) as.integer(levels(x)[x])),
    Map(":", start, end)
)
## Use the sequences to create the new data frame
data.frame(key = sprintf("%03d", unlist(x)), zone = rep(df$zone, lengths(x)))

这使原始数据保持不变并导致以下结果。

  key zone
1 005    3
2 010    5
3 011    5
4 012    5
5 013    5
6 014    7
7 015    7
8 016    7
9 017    7
于 2016-06-15T20:59:41.983 回答
2

这是一个基本的 R 解决方案。

按照@alexis-las 的建议,我创建了一个包含起点和终点的数字矩阵,从而减少了进一步的计算。

# create numeric matrix for future calculations
timeMat <- sapply(df[, 1:2], function(x) as.numeric(as.character(x)))
# get the number of needed row repeats
rowRep <- timeMat[, 2] - timeMat[, 1] + 1
# get the keys
keys <- unlist(sapply(1:3, function(i) timeMat[i, 1]:timeMat[i, 2])

# get data.frame
data.frame("zone"=df$zone[rep(1:nrow(df), rowRep)], "keys"=sprintf("%03d", keys))

zone keys
1    3  005
2    5  010
3    5  011
4    5  012
5    5  013
6    7  014
7    7  015
8    7  016
9    7  017

原始方法没有为键提供零填充,而是返回整数。这是我构建的用于构建填充 0 的冗长函数:

# add zero padding to keys
keys <- paste0(unlist(Map(function(x, y) paste(rep(x, each=y), collapse=""),
                                           rep("0", length(keys)), (3 - nchar(keys)))), keys)

感谢@alexis-laz 的评论指出我@procrastinatus-maximus 的解决方案,这个丑陋的功能可以大大简化sprintf

# add zero padding to keys
keys <- sprintf("%03d", keys)
于 2016-06-15T18:12:13.570 回答
2

一个dplyr/tidyr选项:

library(dplyr)
library(tidyr)

      # make list column grouped by row
x %>% rowwise() %>% 
    # convert factors to character (if not already)
    mutate_each(funs(as.character), -zone) %>%
    # make key list column, drop unmentioned columns
    transmute(key = list(start:end), zone) %>% 
    # unnest list column
    unnest() %>%
    rowwise() %>%
    # add 0s
    mutate(key = paste0(paste(rep('0', 3 - nchar(key)), collapse = ''), key))

# Source: local data frame [9 x 2]
# Groups: <by row>
#     
#    zone   key
#   (dbl) (chr)
# 1     3   005
# 2     5   010
# 3     5   011
# 4     5   012
# 5     5   013
# 6     7   014
# 7     7   015
# 8     7   016
# 9     7   017
于 2016-06-15T17:08:01.693 回答
2

这是我的dplyr方法,借鉴了@ProcrastinatusMaximus 的想法:

library(dplyr)

df %>%
  group_by(zone) %>%
  do(data.frame(key = .$start:.$end)) %>%
  mutate(key = sprintf('%03d', key))

#      zone   key
#     (dbl) (chr)
#   1     3   005
#   2     5   010
#   3     5   011
#   4     5   012
#   5     5   013
#   6     7   014
#   7     7   015
#   8     7   016
#   9     7   017
于 2016-06-15T16:47:34.730 回答