Лучший способ хранить данные переменной длины в R data.frame? - PullRequest
9 голосов
/ 24 февраля 2010

У меня есть данные смешанного типа, которые я хотел бы сохранить в какой-либо структуре данных R. Каждая точка данных имеет набор фиксированных атрибутов, которые могут быть 1-й цифрой, коэффициентами или символами, а также набор данных переменной длины. Например:

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 или lapply(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)))

Но так ли это лучше?

Ответы [ 5 ]

4 голосов
/ 24 февраля 2010

Другой вариант - преобразовать ваш фрейм данных в матрицу списка режимов - каждый элемент матрицы будет списком. стандартные операции с массивами (нарезка с помощью [, 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 голосов
/ 24 февраля 2010

Попытка вставить данные во фрейм данных кажется мне хакерской. Гораздо лучше рассматривать каждую строку как отдельный объект, а затем думать о наборе данных как о массиве этих объектов.

Эта функция преобразует ваши строки данных в соответствующий формат. (Это код стиля 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
4 голосов
/ 24 февраля 2010

Я бы просто использовал данные в "длинном" формате.

* 1003 Е.Г. *

> 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, чтобы извлечь из них то, что вам нужно.

1 голос
/ 24 февраля 2010

Поскольку структура фрейма данных 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
0 голосов
/ 04 марта 2010

Я бы также использовал строки для данных переменной длины, но как в следующем примере: «c (5,5)» для первой фразы. Для выполнения вычислений нужно использовать eval(parse(text=...)).

Например, mean может быть вычислено следующим образом:

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...