Zack
Zack

Reputation: 1231

R dataframe with nested vector

Background

In R this works:

> df <- data.frame(a=numeric(), b=numeric())
> rbind(df, list(a=1, b=2))
  a b
1 1 2

But if I want the list to have a vector, rbind fails:

> df <- data.frame(a=numeric(), b=vector(mode="numeric"))
> rbind(df, list(a=1, b=c(2,3)))
Error in rbind(deparse.level, ...) : 
  invalid list argument: all variables should have the same length

And if I try to specify the vector length, declaring the dataframe fails:

> df <- data.frame(a=numeric(), b=vector(mode="numeric", length=2))
Error in data.frame(a = numeric(), b = vector(mode = "numeric", length = 2)) : 
  arguments imply differing number of rows: 0, 2

Finally, if I eschew declaring the dataframe and try rbind two lists directly, it looks like everything is working, but the datatypes are all wrong, and none of the columns appear to exist.

> l1 <- list(a=1, b=c(2,3))
> l2 <- list(a=10, b=c(20,30))
> obj <- rbind(l1, l2)
> obj
   a  b        
l1 1  Numeric,2
l2 10 Numeric,2
> typeof(obj)
[1] "list"
> obj$a
NULL
> obj$b
NULL
> names(obj)
NULL

My setup

I have a embedded device that gathers data every 50ms and spits out a packet of data. In my script, I'm parsing a waveform that represents the states of that process (process previous frame and transmit, gather new data, dead time where nothing happens) with a state machine. For each packet I'm calculating the duration of the process period, the gathering data period which is subdivided into 8 or 16 acquisition cycles, where I calculate the time of each acquisition cycle, and the remaining dead time.

My list basically looks like `list(process=#, cycles=c(#,#,#,#), deadtime=#). Different packet types have different cycle lengths, so I pass that in as a parameter and I want the script to work on any packet time.

My question

Is there a way to declare a dataframe that does what I want, or am I using R in a fundamentally wrong way and I should break each cycle into it's own list element? I was hoping to avoid the latter as it will make treating the cycles as a group more difficult.

I will note that I've just started learning R so I'm probably doing some odd things with it.

Expected output

If I were to process 4 packets worth of signal with 3 acq. cycles each, this would be my ideal output:

df <- data.frame(processTime=numeric(), cyles=???, deadtime=numeric())
df <- rbind(df, list(processTime=0.05, cycles=c(0.08, 0.10, 0.07), deadtime=0.38)
etc...

  processTime cycles           deadtime
1 0.05        0.08 0.10 0.07   0.38
2 0.06        0.07 0.11 0.09   0.36
3 0.07        0.28 0.11 0.00   0.00
4 0.06        0.08 0.08 0.09   0.41

Upvotes: 3

Views: 4564

Answers (1)

akaDrHouse
akaDrHouse

Reputation: 2250

I'll take a different stab. Dealing with just your first 2 records.

processTime<-c(.05,.06)
cycles<-list(list(.08,.10,.07), list(.07,.09,.38))
deadtime<-c(.38,.36)

For cycles, we created a list element with a list that contains 3 elements in it. So cycles[[1]][1] would refer to .08, and cycles[[1]][2] would refer second element of the first list and cycles[[2]][3] would refer to the 3rd item in the second list.

If we use cbind to bind these we get the following:

test<-as.data.frame(cbind(processTime,cycles,deadtime))
test

  processTime           cycles deadtime
1        0.05 0.08, 0.10, 0.07     0.38
2        0.06 0.07, 0.09, 0.38     0.36

test$cycles[[1]] will return first list

test$cycles[[1]]
[[1]]
[[1]][[1]]
[1] 0.08

[[1]][[2]]
[1] 0.1

[[1]][[3]]
[1] 0.07

Whereas the 3rd element of the second list can be called with:

test$cycles[[2]][3]
[[1]]
[1] 0.38

You can also unlist later for calculations:

unlist(test$cycles[[2]])
[1] 0.07 0.09 0.38

To do this iteratively as you requested.

test<-data.frame()
     processTime<-c(.05)
     cycles<-list(list(.08,.10,.07))
     deadtime<-c(.38)
     test<-as.data.frame(cbind(processTime,cycles,deadtime))
test

  processTime           cycles deadtime
1        0.05 0.08, 0.10, 0.07     0.38  


     processTime<-c(.06)
     cycles<-list(list(.07,.09,.38))
     deadtime<-c(.36)
test<- rbind(test,as.data.frame(cbind(processTime,cycles,deadtime)))
test 

  processTime           cycles deadtime
1        0.05 0.08, 0.10, 0.07     0.38
2        0.06 0.07, 0.09, 0.38     0.36

Upvotes: 1

Related Questions