【问题标题】:Best way to store variable-length data in an R data.frame?将可变长度数据存储在 R data.frame 中的最佳方法?
【发布时间】:2011-01-20 06:38:36
【问题描述】:

我有一些混合类型的数据,我想将它们存储在某种 R 数据结构中。每个数据点都有一组固定属性,可以是一维数字、因子或字符,还有一组可变长度数据。例如:

id  phrase                    num_tokens  token_lengths
1   "hello world"             2           5 5
2   "greetings"               1           9
3   "take me to your leader"  4           4 2 2 4 6

实际值并非都可以相互计算,但这就是数据的特点。我要做的操作包括基于布尔函数(例如nchar(data$phrase) > 10lapply(data$token_lengths, length) > 2)之类的东西)对数据进行子集化。我还想按索引对可变长度部分中的值进行索引和平均值。这不会不行,但类似:mean(data$token_lengths[1], na.rm=TRUE))

我发现我可以通过将“token_lengths”设置为数组来将它塞入 data.frame:

d <- data.frame(id=c(1,2,3), ..., token_lengths=as.array(list(c(5,5), 9, c(4,2,2,4,6)))

但这是最好的方法吗?

【问题讨论】:

  • 在平均时也许你想要lapply(data$token_lengths, mean, na.rm=TRUE)?但我不完全明白你想要什么。

标签: r dataframe


【解决方案1】:

尝试将数据硬塞到数据框中对我来说似乎很不自然。最好将每一行视为一个单独的对象,然后将数据集视为这些对象的数组。

此函数将您的数据字符串转换为适当的格式。 (这是 S3 风格的代码;您可能更喜欢使用“适当的”面向对象系统之一。)

as.mydata <- function(x)
{
   UseMethod("as.mydata")
}

as.mydata.character <- function(x)
{
   convert <- function(x)
   {
      md <- list()
      md$phrase = x
      spl <- strsplit(x, " ")[[1]]
      md$num_words <- length(spl)
      md$token_lengths <- nchar(spl)
      class(md) <- "mydata"
      md
   }
   lapply(x, convert)
}

现在你的整个数据集看起来像

mydataset <- as.mydata(c("hello world", "greetings", "take me to your leader"))

mydataset
[[1]]
$phrase
[1] "hello world"

$num_words
[1] 2

$token_lengths
[1] 5 5

attr(,"class")
[1] "mydata"

[[2]]
$phrase
[1] "greetings"

$num_words
[1] 1

$token_lengths
[1] 9

attr(,"class")
[1] "mydata"

[[3]]
$phrase
[1] "take me to your leader"

$num_words
[1] 5

$token_lengths
[1] 4 2 2 4 6

attr(,"class")
[1] "mydata"

你可以定义一个打印方法来让它看起来更漂亮。

print.mydata <- function(x)
{
   cat(x$phrase, "consists of", x$num_words, "words, with", paste(x$token_lengths, collapse=", "), "letters.")
}
mydataset
[[1]]
hello world consists of 2 words, with 5, 5 letters.
[[2]]
greetings consists of 1 words, with 9 letters.
[[3]]
take me to your leader consists of 5 words, with 4, 2, 2, 4, 6 letters.

对于这种格式的数据,您想要执行的示例操作相当简单。

sapply(mydataset, function(x) nchar(x$phrase) > 10)
[1]  TRUE FALSE  TRUE

【讨论】:

  • 我也打算推荐这个基于列表的解决方案。这当然是你在 R 以外的地方会做的事情。但是有一种方式,all R 编程是“hackish”,以一种很好的方式,并且 data.frames 的(过度)使用是其中一种方式。可以说,长格式 data.frame 可能是程序员效率最高的选择,即使从数据结构的角度来看它有点傻。
  • 那么计算平均令牌数的有效方法是什么?在我原来的例子中,它只是mean(mydata$num_tokens)。对于基于列表的解决方案,您必须执行mean(sapply(mydataset, function(x) x$num_tokens)) 之类的操作。有了辅助函数,当然会更漂亮。
  • @Nick:是的,这样的语法有点笨拙。我会将 sapply 语句放在像 get_num_tokens &lt;- function(x) sapply(x, function(x) x$num_tokens) 这样的函数中。然后你使用mean(get_num_tokens(mydataset))
【解决方案2】:

我只会使用“长”格式的数据。

例如

> d1 <- data.frame(id=1:3, num_words=c(2,1,4), phrase=c("hello world", "greetings", "take me to your leader"))
> d2 <- data.frame(id=c(rep(1,2), rep(2,1), rep(3,5)), token_length=c(5,5,9,4,2,2,4,6))
> d2$tokenid <- with(d2, ave(token_length, id, FUN=seq_along))
> d <- merge(d1,d2)
> subset(d, nchar(phrase) > 10)
  id num_words                 phrase token_length tokenid
1  1         2            hello world            5       1
2  1         2            hello world            5       2
4  3         4 take me to your leader            4       1
5  3         4 take me to your leader            2       2
6  3         4 take me to your leader            2       3
7  3         4 take me to your leader            4       4
8  3         4 take me to your leader            6       5
> with(d, tapply(token_length, id, mean))
  1   2   3 
5.0 9.0 3.6 

一旦数据为长格式,您可以使用 sqldf 或 plyr 从中提取您想要的内容。

【讨论】:

  • 我实际上已经有了这种长格式的数据,我正在尝试缩短它,因为我发现使用它很尴尬。例如,要计算令牌的平均数量,我必须编写如下内容:mean(unique(d[c('id, 'num_tokens')])$num_tokens)。如果数据不长,我可以写mean(d$num_tokens),这样更易​​读。这里主要感兴趣的是短语,它恰好有与之相关的可变长度数据;扩展这些数据会很尴尬。
  • 你可以通过 mean(subset(d, tokenid==1, num_tokens)) 来缩短它,但我明白你的意思。如果您想坚持使用数据框,我认为您可以。想想看:数据帧是相同长度的向量列表。您可以使标记向量成为列表向量: df
  • tokenid 子集至少更有吸引力一点。 :)
【解决方案3】:

另一种选择是将您的数据框转换为模式列表矩阵 - 矩阵的每个元素都是一个列表。标准数组操作(可以使用[、apply() 等进行切片)。

> d <- data.frame(id=c(1,2,3), num_tokens=c(2,1,4), token_lengths=as.array(list(c(5,5), 9, c(4,2,2,4,6))))
> m <- as.matrix(d)
> mode(m)
[1] "list"
> m[,"token_lengths"]
[[1]]
[1] 5 5

[[2]]
[1] 9

[[3]]
[1] 4 2 2 4 6

> m[3,]
$id
[1] 3

$num_tokens
[1] 4

$token_lengths
[1] 4 2 2 4 6

【讨论】:

    【解决方案4】:

    由于 R 数据帧结构松散地基于 SQL 表,因此数据帧的每个元素都不是原子数据类型是不常见的。但是,正如您所展示的,它是可以完成的,这个链接的post 描述了这样一个在更大范围内实现的应用程序。

    另一种方法是将您的数据存储为字符串并具有检索它的函数,或者创建一个单独的函数来附加数据并使用存储在数据框中的索引提取它。

    > ## alternative 1
    > tokens <- function(x,i=TRUE) Map(as.numeric,strsplit(x[i],","))
    > d <- data.frame(id=c(1,2,3), token_lengths=c("5,5", "9", "4,2,2,4,6"))
    > 
    > tokens(d$token_lengths)
    [[1]]
    [1] 5 5
    
    [[2]]
    [1] 9
    
    [[3]]
    [1] 4 2 2 4 6
    
    > tokens(d$token_lengths,2:3)
    [[1]]
    [1] 9
    
    [[2]]
    [1] 4 2 2 4 6
    
    > 
    > ## alternative 2
    > retrieve <- local({
    +   token_lengths <- list(c(5,5), 9, c(4,2,2,4,6))
    +   function(i) token_lengths[i]
    + })
    > 
    > d <- data.frame(id=c(1,2,3), token_lengths=1:3)
    > retrieve(d$token_lengths[2:3])
    [[1]]
    [1] 9
    
    [[2]]
    [1] 4 2 2 4 6
    

    【讨论】:

    • 我想到了 pack-as-string 解决方案,但后来处理可变长度数据变得复杂。现在,我将使用 column-of-arrays 解决方案,并大量使用mapply()。例如,如果我想要每个短语的平均标记长度只是 mapply(mean, d$token_lengths)。如果我想要所有令牌长度的最大值,它是max(mapply(max, d$token_lengths))
    【解决方案5】:

    我也将字符串用于可变长度数据,但如下例所示:“c(5,5)”用于第一个短语。需要使用eval(parse(text=...))进行计算。

    例如,mean 可以如下计算:

    sapply(data$token_lengths,function(str) mean(eval(parse(text=str))))

    【讨论】:

      猜你喜欢
      • 2019-10-11
      • 2021-04-13
      • 2015-08-01
      • 2011-06-27
      • 1970-01-01
      • 2011-06-26
      • 1970-01-01
      • 2011-05-24
      • 2013-10-09
      相关资源
      最近更新 更多