derdepe
derdepe

Reputation: 113

return multiple values from sapply/lapply

I´m new to R and trying to replace some for loops with fuctions from the apply family. I still don´t understand totally how they work but I managed to produce a working piece of code:

#create some input data
tech<-data.frame(cbind(c("p1","p2","p3","p4"),c(15,15,15,100),c(10,8,18,100)))
colnames(tech)=c("id","capacity.el","capacity.th")
tech$capacity.el<-as.numeric(tech$capacity.el)
tech$capacity.th<-as.numeric(tech$capacity.th)

heat<-data.frame(cbind(c(2,12,6,20,32,21,25,16,34,0),c(31,18,3,27,30,31,18,4,24,7),c(2,12,6,20,32,21,25,16,34,0),c(31,18,3,27,30,31,18,4,24,7)))
colnames(heat)=c("p1","p2","p3","p4")

> tech
  id capacity.el capacity.th
1 p1           2           1
2 p2           2           4
3 p3           2           3
4 p4           1           2


> heat
   p1 p2 p3 p4
1   2 31  2 31
2  12 18 12 18
3   6  3  6  3
4  20 27 20 27
5  32 30 32 30
6  21 31 21 31
7  25 18 25 18
8  16  4 16  4
9  34 24 34 24
10  0  7  0  7

#the result should be a matrix/list 
pel=matrix(,nrow=nrow(heat),ncol=ncol(heat))
epr=matrix(,nrow=nrow(heat),ncol=ncol(heat))        
result<-list()

#main code    
result<-sapply(colnames(heat),function(x) {
                a<-tech$capacity.th[match(x,tech$id)]
                b<-tech$capacity.el[match(x,tech$id)]
                sapply(heat[,x],function(y) {
                  pel<-a*y
                  return(pel)
                })

              })

The idea is to "loop" through the columns of the "heat" data.frame and perform some calculations with values from the "heat" data.frame. For this reason I use the first sapply fuction to get the corresponding characteristics for each of the plants in the heat table from the tech table. The second sapply then performs the calculations. The output "result" is exactly what I wanted.

Now I want to calculate more than value from each row in "heat" (pel and epr). But I have no idea how to extract these values from within the sapply fuctions. I tried the following with a list but this extracts the values as one large matrix with 20 rows. The perfect result would be something like a list with two matrix or data.frame objects that each have 10 rows and 4 columns with the pel/epr values.

result<-sapply(colnames(heat),function(x) {
                a<-tech$capacity.th[match(x,tech$id)]
                b<-tech$capacity.el[match(x,tech$id)]
                sapply(heat[,x],function(y) {
                  pel<-a*y
                  epr<-b*y
                })
                new<-list(pel,epr)
                return(new)
              })

I would appreciate any help or comment.

Upvotes: 5

Views: 4690

Answers (3)

akrun
akrun

Reputation: 887108

A possible option using data.table (similar approach as @user2992199's dplyr method). We convert the 'data.frame' to 'data.table' (setDT(heat)), change from 'wide' to 'long' format with melt, set the key as 'id' (setkey(..., id)), join with 'tech', and create the columns 'a' and 'b'.

library(data.table)#v1.9.5+
setkey(melt(setDT(heat), variable.name='id'), id)[tech
  ][, c('a', 'b') := list(value*capacity.el, value*capacity.th)][]

Upvotes: 2

MarkusN
MarkusN

Reputation: 3223

I suggest you tidy up your data first. see the tidyr package for more information

Then you merge the two dataframes, and you won't need any loops or *apply functions. Your simply do your calculations within this new dataframe, for example by using the dplyr package:

library(tidyr)
library(dplyr)

heat %>%
  gather(id, value) %>%
  left_join(tech, by="id") %>%
  mutate(a = value * capacity.el,
         b = value * capacity.th)

Upvotes: 6

bgoldst
bgoldst

Reputation: 35314

o <- match(tech$id,names(heat)); ## precompute tech row order to match heat column order
ms <- names(tech[-1]); ## store multiplier column names from tech
setNames(lapply(ms,function(m) t(t(heat)*tech[o,m])),ms);
## $capacity.el
##       p1 p2 p3 p4
##  [1,]  4 62  4 31
##  [2,] 24 36 24 18
##  [3,] 12  6 12  3
##  [4,] 40 54 40 27
##  [5,] 64 60 64 30
##  [6,] 42 62 42 31
##  [7,] 50 36 50 18
##  [8,] 32  8 32  4
##  [9,] 68 48 68 24
## [10,]  0 14  0  7
##
## $capacity.th
##       p1  p2  p3 p4
##  [1,]  2 124   6 62
##  [2,] 12  72  36 36
##  [3,]  6  12  18  6
##  [4,] 20 108  60 54
##  [5,] 32 120  96 60
##  [6,] 21 124  63 62
##  [7,] 25  72  75 36
##  [8,] 16  16  48  8
##  [9,] 34  96 102 48
## [10,]  0  28   0 14

Upvotes: 3

Related Questions