我有一个数据集,沿着中心骨架的每个像素位置都有宽度。宽度输出为逗号分隔的单个字符串。
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
答案 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)][]