拆分字符串和转置结果

时间:2016-05-16 19:43:51

标签: r data.table dplyr

我有一个数据集,沿着中心骨架的每个像素位置都有宽度。宽度输出为逗号分隔的单个字符串。

cukeDatatest <- read.delim("https://gist.githubusercontent.com/bhive01/e7508f552db0415fec1749d0a390c8e5/raw/a12386d43c936c2f73d550dfdaecb8e453d19cfe/widthtest.tsv")
str(cukeDatatest) # or dplyr::glimpse(cukeDatatest)

我需要保留File和FruitNum标识符的宽度。

我想要的输出有三列File,FruitNum,ObjectWidth,但File和FruitNum重复了该水果的ObjectWidth长度。位置很重要,因此对这些向量进行排序会非常糟糕。此外,每种水果的长度都不同(如果这对您的方法很重要)。

我之前使用str_split()从字符串中剖析了一些元素,但从来没有这么大的东西,也没有那么多(我有8000个)。处理时间是一个问题,但会等待正确的结果。

我比data.table更习惯于dplyr,但我看到Arun在这方面做了一些努力:R split text string in a data.table columns

3 个答案:

答案 0 :(得分:5)

使用splitstackshape

library(splitstackshape)
res <- cSplit(cukeDatatest, splitCols = "ObjectWidth", sep = ",", direction = "long")

# result
head(res)
#                            File FruitNum ObjectWidth
# 1: IMG_7888.JPGcolcorrected.jpg        1           4
# 2: IMG_7888.JPGcolcorrected.jpg        1          10
# 3: IMG_7888.JPGcolcorrected.jpg        1          14
# 4: IMG_7888.JPGcolcorrected.jpg        1          15
# 5: IMG_7888.JPGcolcorrected.jpg        1          22
# 6: IMG_7888.JPGcolcorrected.jpg        1          26

答案 1 :(得分:4)

Hadleyverse选项,其中包含一些合理的类型转换:

library(dplyr)
library(tidyr)

cukeDatatest %>% 
    # split ObjectWidth into a nested column containing a vector
    mutate(ObjectWidth = strsplit(as.character(.$ObjectWidth), ',')) %>% 
    # unnest nested column, melting data to long form
    unnest() %>% 
    # convert data to integer
    mutate(ObjectWidth = as.integer(ObjectWidth))

# Source: local data frame [39,830 x 3]
# 
#                            File FruitNum ObjectWidth
#                          (fctr)    (int)       (int)
# 1  IMG_7888.JPGcolcorrected.jpg        1           4
# 2  IMG_7888.JPGcolcorrected.jpg        1          10
# 3  IMG_7888.JPGcolcorrected.jpg        1          14
# 4  IMG_7888.JPGcolcorrected.jpg        1          15
# 5  IMG_7888.JPGcolcorrected.jpg        1          22
# 6  IMG_7888.JPGcolcorrected.jpg        1          26
# 7  IMG_7888.JPGcolcorrected.jpg        1          26
# 8  IMG_7888.JPGcolcorrected.jpg        1          28
# 9  IMG_7888.JPGcolcorrected.jpg        1          34
# 10 IMG_7888.JPGcolcorrected.jpg        1          35
# ..                          ...      ...         ...

修改

这是一个具有更典型的tidyr方法的等效版本。这种方法的一个问题是ObjectWidth中的术语数量不规则会使列名有些困难,因为separate令人烦恼地不包含其into参数的默认值。

这里一个简单的解决方法是故意创建比您需要的更多的列(其余的将填充NA,随后将被gather删除。虽然效率低于理想效果,但代码仍然是即时运行的,因此对性能影响不够。如果它有问题,请用max(sapply(strsplit(as.character(cukeDatatest$ObjectWidth), ','), length))计算出最长行的长度。

cukeDatatest %>%
    # tbl_df conversion is unnecessary, but nice for printing purposes
    tbl_df() %>%
    # split ObjectWidth on commas into individual columns
    separate(ObjectWidth, into = paste0('X', 1:2500), 
        sep = ',', fill = 'right', convert = TRUE) %>% 
    # gather into long form
    gather(var, ObjectWidth, starts_with('X'), na.rm = TRUE) %>% 
    # remove key column identifying term number within initial ObjectWidth string
    select(-var)

如果每个ObjectWidth字符串中包含固定数量的字词,那么粘贴在read.csv上的普通旧ObjectWidth是一个很好的方法。 read.csv估计前五行的列数,如果数字是常数则可以。

如果这不起作用(对于此数据,最长行是第七行),您将遇到与上面相同的名称问题,可以通过提供col.names一组名称进行排序适当的长度。如有必要,上述相同的解决方法也适用于此。

read.csv(text = paste(as.character(cukeDatatest$ObjectWidth), collapse = '\n'), 
         header = FALSE, col.names = paste0('V', 1:2179)) %>% 
    bind_cols(cukeDatatest[,-3]) %>% 
    gather(var, ObjectWidth, starts_with('V'), na.rm = TRUE) %>% 
    select(-var)

两种方法都返回一个完全等同于上述原始方法的tbl_df。

答案 2 :(得分:4)

我通常从简单的strsplit开始:

dt[, strsplit(ObjectWidth, ",", fixed = T)[[1]], by = .(File, FruitNum)]

如果这太慢了,我会在整个列上运行strsplit,然后根据自己的喜好重新排列数据:

l = strsplit(dt$ObjectWidth, ",", fixed = T)

dt[inverse.rle(list(lengths = lengths(l), values = seq_along(l))),
   .(File, FruitNum)][, col := unlist(l)][]