mbrandalero
mbrandalero

Reputation: 416

Apply custom scaling model to data frame?

Apologies in advance for the long post; I'm new to this part of R so I don't know exactly how to summarize the question.

I have a data frame for "baseline results" that looks like this

Circuit Voltage Power
      A       1     1
      B       1     3
      C       1     5

And a scaling model that looks like this

Voltage Scaled_Power
    1.0         1.00
    0.9         0.75
    0.8         0.50

This scaling model comes from a table, not from any function such as lm. This model essentially says: "if new Voltage == 0.9, then scale the baseline Power by 0.75". The scaling model is independent of "Circuit", but requires the baseline Power for the specific "Circuit" to calculate new power. The end result I want to achieve is this:

Circuit Voltage Power
      A     1.0  1.00
      B     1.0  3.00
      C     1.0  5.00
      A     0.9  0.75
      B     0.9  2.25
      C     0.9  3.75
      A     0.8  0.50
      B     0.8  1.50
      C     0.8  2.50

that is, a new data frame with all combinations of "Circuit" from the first data frame and "Voltage" from the second (I know I can do this with expand.grid), but with a new column "Power" which applies a scaling factor.

This sounds like pattern commonly solved, so I guess a solution must already exist?


I've tried the following so far

expand.grid(Circuit = df_base$Circuit,Voltage = scaling_model$Voltage) %>%
   left_join(df_base) %>% 
   left_join(scaling_model)

Circuit Voltage Power Scaled_Power
      A     1.0     1         1.00
      B     1.0     3         1.00
      C     1.0     5         1.00
      A     0.9    NA         0.75
      B     0.9    NA         0.75
      C     0.9    NA         0.75
      A     0.8    NA         0.50
      B     0.8    NA         0.50
      C     0.8    NA         0.50

but then I need some function for doing multiplication selecting a a specific value from a row (something like Excel's VLOOKUP), and I'm not sure this is the most R-ish way to solve the problem.


Here's the code for the MWE

library(dplyr)
scaling_model <-
    data.frame(
        Voltage      = c(1.0,  0.9, 0.8),
        Scaled_Power = c(1.0, 0.75, 0.5)) 
df_base <- data.frame(
        Circuit = factor(c("A", "B", "C")),
        Voltage = c(1.0, 1.0, 1.0),
        Power   = c(1.0, 3.0, 5.0))
df_result <-
    data.frame(
        Circuit = factor(c("A", "B", "C", "A", "B", "C", "A", "B", "C")),
        Voltage = c(1.0, 1.0, 1.0, 0.9, 0.9, 0.9, 0.8, 0.8, 0.8),
        Power   = c(1.0, 3.0, 5.0,
                    1.0 * 0.75, 3.0 * 0.75, 5.0 * 0.75,
                    1.0 * 0.5, 3.0 * 0.5, 5.0 * 0.5))

Upvotes: 2

Views: 133

Answers (2)

markus
markus

Reputation: 26363

With dplyr you could do

library(dplyr)
expand.grid(Circuit = df_base$Circuit,
            Voltage = scaling_model$Voltage) %>% 
  left_join(scaling_model) %>% 
  left_join(df_base[c("Circuit", "Power")]) %>% 
  mutate(Power = Scaled_Power * Power) %>%
  select(-Scaled_Power)
#  Circuit Voltage Power
#1       A     1.0  1.00
#2       B     1.0  3.00
#3       C     1.0  5.00
#4       A     0.9  0.75
#5       B     0.9  2.25
#6       C     0.9  3.75
#7       A     0.8  0.50
#8       B     0.8  1.50
#9       C     0.8  2.50

Here is an another base R option that uses expand.grid and match

df_result <- expand.grid(Circuit = df_base$Circuit,
                         Voltage = scaling_model$Voltage)

df_result$Power <- df_base$Power * scaling_model$Scaled_Power[match(df_result$Voltage, scaling_model$Voltage)]
df_result

match finds the positions of df_result$Voltage in scaling_model$Voltage and we use the result to subset (i.e. expand) scaling_model$Scaled_Power which we finally multiply by df_base$Power.

Upvotes: 2

BENY
BENY

Reputation: 323366

Create a merge key , then doing merge

scaling_model$mergekey=1
df_base$mergekey=1
newdf=merge(scaling_model,df_base[,c('Circuit','mergekey','Power')],by='mergekey')
newdf$Scaled_Power=newdf$Scaled_Power*newdf$Power
newdf
  mergekey Voltage Scaled_Power Circuit Power
1        1     1.0         1.00       A     1
2        1     1.0         3.00       B     3
3        1     1.0         5.00       C     5
4        1     0.9         0.75       A     1
5        1     0.9         2.25       B     3
6        1     0.9         3.75       C     5
7        1     0.8         0.50       A     1
8        1     0.8         1.50       B     3
9        1     0.8         2.50       C     5

Upvotes: 1

Related Questions