I'm rather new to R and there's probably a really easy way to do this, but I can not figure this out for the life of me. I am trying to convert a mutli-layer TIF file into a multi-channel TIF file and to do that I need to basically turn a list of multiple 2-dimensional arrays into one 3-dimensional array.
I attempted to simply split up the 9 different arrays in the list, create a new list with 3 dimensions and insert the arrays into the new list, but it keeps telling me that there is a "wrong number of subscripts".
multiChannelFile = array(c(960, 1280, 9))
for (row in 1:960) {
for (column in 1:1280) {
for (channel in 1:9) {
multiChannelFile[row, column, channel] = tifFile[channel][[1]][row, column]
}
}
}
You can use simplify2array():
multiChannelFile <- simplify2array(tifFile)
And of course, as soon as I posted this question, I figured it out.
Changing multiChannelFile = array(c(960, 1280, 9)) to multiChannelFile = array(dim = c(960, 1280, 9)) did the trick.
You can blend the list to a vector by unlist() and reset the dimension.
array(unlist(tifFile), dim = c(dim(tifFile[[1]]), length(tifFile)))
Related
Im trying to expand the size of multidimensional array.
More particullary I have an 10x3x3 (rowsxcolumnsxmatrix) array and I want to expand it to become an 20x3x3 array.
I want to keep my current data in the first 10 rows and just resize their rows so I can add more data. Ideally the new rows should have 0s.
I can do this with loops but Im wondering if there is a function that can do the whole process automatically.
Thanks in advance
You can use the library abind.
library(abind)
v1=numeric(10)
v2=numeric(3)
a=array(c(v1, v2), dim = c(10, 3, 3))
res <- abind(originalArray, a, along = 1)
I am running a for loop to fill dynamically a dataframe (I know a baby seal dies somewhere because I am using a for loop)
I have something like this in mind (the 5 is a placeholder for a function that returns a scalar):
results<-data.frame(matrix(NA, nrow = length(seq(1:10)), ncol =
length(seq(1:10))))
rows<-data.frame(matrix(NA, nrow = 1, ncol = 1))
for (j in seq(1:10)){
rows<-data.frame()
for (i in seq(1:10)){
rows<-cbind(rows,5)
}
results<-cbind(results,rows)
}
I get the following error message with my approach above.
Error in match.names(clabs, names(xi)) :
names do not match previous names
Is there an easier way?
Dynamically filling an object using a for loop is fine - what causes problems is when you dynamically build an object using a for loop (e.g. using cbind and rbind rows).
When you build something dynamically, R has to go and request new memory for the object in each loop, because it keeps increasing in size. This causes a for loop to slow down with every iteration as the object gets bigger.
When you create the object beforehand (e.g. a data.frame with the right number of rows and columns), and fill it in by index, the for loop doesn't have this problem.
One final thing to keep in mind is that for data.frames (and matrices) each column is stored as a vector in memory – so its usually more efficient to fill these in one column at a time.
With all that in mind we can revise your code as follows:
results <- data.frame(matrix(NA, nrow = length(seq(1:10)),
ncol = length(seq(1:10))))
for (rowIdx in 1:nrow(results)) {
for (colIdx in 1:ncol(results)) {
results[rowIdx, colIdx] <- 5 # or whatever value you want here
}
}
Not sure what is your intention. Now keeping your intention and way of implementation a way to fix the problem to change for-loop so that rows is initialized with 1st value. The second for-loop should be from seq(2:10).
The error is occurring because attempting to cbind a blank data.frame with valid value.
for (j in seq(1:10)){
rows<-data.frame(5) #Initialization with 1st value
for (i in seq(2:10)){ #Loop 2nd on wards.
rows<-cbind(rows,5)
}
results<-cbind(results,rows)
}
Or how to split a vector into pairs of contiguous members and combine them in a list?
Supose you are given the vector
map <-seq(from = 1, to = 20, by = 4)
which is
1 5 9 13 17
My goal is to create the following list
path <- list(c(1,5), c(5,9), c(9,13), c(13,17))
This is supposed to represent the several path segments that the map is sugesting us to follow. In order to go from 1 to 17, we must first take the first path (path[1]), then the second path (path[2]), and all the way to the end.
My first attempt lead me to:
path <- split(aux <- data.frame(S = map[-length(map)], E = map[-1]), row(aux))
But I think it would be possible without creating this auxiliar data frame
and avoiding the performance decrease when the initial vector (the map) is to big. Also, it returns a warning message which is quite alright, but I like to avoid them.
Then I found this here on stackoverflow (not exactly like this, this is the adapted version for my problem):
mod_map <- c(map, map[c(-1,-length(map))])
mod_map <- sort(mod_map)
split(mod_map, ceiling(seq_along(mod_map)/2))
which is a simpler solution, but I have to use this modified version of my map.
Pherhaps I'm asking too much as I already got two solutions. But, could it be possible to have a third one, so that I don't have so use data frames as in my first solution and can use the original map, unlike my second solution?
We can use Map on the vector ('map' - better not to use function names - it is a function from purrr) with 1st and last element removed and concatenate elementwise
Map(c, map[-length(map)], map[-1])
Or as #Sotos mentioned, split can be used which would be faster
split(cbind(map[-length(map)], map[-1]), seq(length(map)-1))
Thanks for reading. I am new to functions, and want to make my scripts much more readable as they will be shared around and wondered if you could help.
I used to have this peice of scrip to create multiple new dataframes from a 3D data set.
for (i in 1:ncol(x$data))
{nam <- paste("timepoint",deparse(i), sep=".")
assign(nam, 'as.data.frame(print(x$data[,i,])))}
Now I want this as a function, not sure on the best return format, but likely a list so that my data is contained within the list as listname$timepoint1
So far I have....
funcs <- list()
step2<-function(funcs, x){
for(i in ncol(x$data))
{
timepoint <- paste( "timepoint", i, sep = '' )
funcs[[timepoint]] = assign(timepoint[i],(print(x$data[,i,])))
}
return(funcs)
}
tester<-step2(funcs,x)
this works, except it only returns the final timepoint. giving at the end....
summary(tester)
Length Class Mode
timepoint79 25600 -none- numeric
Thanks alot
So using a for loop I was able to break my 1.1 million row dataset in r into 110 tables of approximately 10,000 rows each in hopes of getting r to handle the data better. I now want to run another for loop that assigns the values in each of these tables to a different dataframe name.
My table names are:
Pom_1
Pom_2
Pom_3
...
Pom_110
What I want to do is create a for loop like the following:
for (i in 1:110)
{
Pom <- read.table(paste("Pom",i,sep = "_"))
for (j in 1:nrows(Pom))
{do something}
}
So I want to loop through the array and assign the values of each Pom table to "Pom" so that I can then run a for loop on each subsection of Pom. This problem is the read.table function does not seem to be the right one. Any ideas?
Can you give a more specific example of what you want to do withing each dataframe? You should avoid using the inner loop when possible and if you really need to have a look at ?apply
nrow instead of nrows
This is a generic solution using a example data.frame. The function you're looking for is assign, check it's help page:
Pom = data.frame(x = rnorm(30)) #original data.frame
n.tables = 3 # number of new data.frames you want to creat
Pom.names = paste("Pom",1:3,sep="") # name of all new data.frames
breaks = nrow(Pom)/n.tables * 0:n.tables # breaks of the original data.frame
for (i in 1:n.tables) {
rows = (breaks[i]+1):breaks[i+1] # which rows from Pom are going to be assign to the new data.frame?
assign(Pom.names[i],Pom[rows,]) # create new data.frame
}
ls()
[1] "breaks" "i" "n.tables" "Pom" "Pom.names" "Pom1"
[7] "Pom2" "Pom3" "rows"
I'm willing to bet the problem with your table call is that you aren't specifying the file extension (assuming Pom_1 - Pom_110 are files in your working directory, which I think they are since you're using read.table).
You can fix it by the following
fileExtension<-".xls" #specify your extension, I assume xls
for (i in 1:110)
{
tablename<-paste("Pom",i,sep = "_")
Pom <- read.table(paste(tablename, fileExtension, sep=""))
for (j in 1:nrows(Pom))
{do something}
}
Of course that's assuming a couple things about how everything in your problem is set up, but it's my best guess based on your description and code