在R data.frame中存储可变长度数据的最佳方法?

在R data.frame中存储可变长度数据的最佳方法?,r,dataframe,R,Dataframe,我有一些混合类型的数据,我想存储在某种R数据结构中。每个数据点都有一组固定属性,这些属性可以是一维数字、因子或字符,也可以是一组可变长度的数据。例如: id phrase num_tokens token_lengths 1 "hello world" 2 5 5 2 "greetings" 1 9 3 "take me to your leader"

我有一些混合类型的数据,我想存储在某种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)>10
lappy(data$token\u length,length)>2)
。我还想按索引对可变长度部分中的值和平均值进行索引。这不起作用,但类似于:
mean(data$token_length[1],na.rm=TRUE))

我发现我可以通过将其设置为数组将“token_length”塞进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)))

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

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

> ## 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
##备选方案1
>代币d
>代币(d$代币长度)
[[1]]
[1] 5 5
[[2]]
[1] 9
[[3]]
[1] 4 2 2 4 6
>代币(d$代币长度,2:3)
[[1]]
[1] 9
[[2]]
[1] 4 2 2 4 6
> 
>##备选方案2
>检索d检索(d$token_长度[2:3])
[[1]]
[1] 9
[[2]]
[1] 4 2 2 4 6

试图将数据硬塞进数据框对我来说似乎有点不妥。最好将每个行视为单个对象,然后将数据集看作这些对象的数组。

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


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

例如

>d1 d2 d2$tokenid d子集(d,nchar(短语)>10)
id num\u单词短语标记\u长度标记id
1 1 2你好世界5 1
2 1 2你好世界5 2
4 3 4带我去见你的领导4 1
5 3 4带我去见你的领导2
6 3 4带我去见你的领导2 3
7 3 4带我去见你的领导4 4
8 3 4带我去见你的领导6 5
>带(d,tapply(标记长度,id,平均值))
1   2   3 
5.0 9.0 3.6 

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

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

>d m模式(m)
[1] “列表”
>m[,“标记长度”]
[[1]]
[1] 5 5
[[2]]
[1] 9
[[3]]
[1] 4 2 2 4 6
>m[3,]
$id
[1] 3
$num_代币
[1] 4
$token_长度
[1] 4 2 2 4 6

对于可变长度的数据,我也会使用字符串,但如下面的示例所示,第一个短语是“c(5,5)”。需要使用
eval(parse(text=…)
来执行计算

例如,
平均值
可计算如下:


sapply(数据$token\u长度,函数(str)平均值(eval(parse(text=str)))

在求平均值时,可能需要
lapply(数据$token\u长度,平均值,na.rm=TRUE)
?但我不完全理解你想要什么。我也打算建议这个基于列表的解决方案。这当然是你在R以外的其他地方会做的。但是有一种方式,所有R编程都是“黑客式的”,以一种好的方式,并且(结束)使用data.frames就是其中一种方法。可以说,长格式的data.frames可能是程序员最有效的选择,即使从数据结构的角度来看这有点傻。那么,计算令牌平均数量的有效方法是什么呢?在我最初的示例中,它只是
mean(mydata$num_令牌)
。对于基于列表的解决方案,您必须执行类似于
mean的操作(sapply(mydataset,function(x)x$num_tokens))
。如果使用帮助函数,那当然会更漂亮。@Nick:是的,这样的语法会有点笨拙。我会把sapply语句放在一个函数中,比如
get_num_tokens,我实际上已经有了这么长格式的数据,我正试图缩短它,因为我发现使用它有点笨拙。例如,计算平均值我必须编写的令牌数量如下:
mean(唯一的(d[c('id,'num\u tokens'))$num\u tokens)
。如果数据不长,我可以只编写
mean(d$num\u tokens)
更具可读性。这里主要关注的是短语,它恰好有可变长度的数据与之关联;扩展这些数据会让它变得笨拙。您可以通过平均值来缩短它(子集(d,tokenid==1,num_tokens)),但我明白你的意思。如果你想坚持使用数据帧,我想你可以。想想看:数据帧是相同长度的向量列表。你可以让令牌向量成为列表向量:df tokenid子集至少更吸引人一点。:)我认为包是字符串解决方案,但使用起来很复杂可变长度数据。现在,我将使用数组列解决方案,并大量使用
mapply()
。例如,如果我想要每个短语的平均标记长度,它只是
mappy(mean,d$token\u length)
。如果我想要所有标记长度的最大值,它是
max(mappy(max,d$token\u length))
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
> 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 
> 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