DarrenRhodes
DarrenRhodes

Reputation: 1503

How to prepare a 3rd data.frame from two others

I've constructed a data.frame using the inefficient code below. Can you improve it, noting that if you can think of a better starting point, please include that answer.

My code takes data from the first two data frames and combines them to give the third. The first data.frame is a grid of 1s and -1s representing low or high values. The second data.frame includes all the information for me to calculate the high or low values. Note that each column has similar calculations but the calculation may differ from column to column.

## Exampple Data for Question
## How to prepare a 3rd data.frame from two others

## Prepare 1st data.frame, a Yates table

A <- B <- C <- c(1,-1)
yates.1 <- expand.grid(A = A, B = B, C = C)

## Prepare 2nd data.frame with the reaction data
reaction.info <- data.frame(stringsAsFactors = FALSE,
                            factor.name = c("A", "B", "C"),
                            Component = c("Water", "SM", "Reagent"),
                            Mw = c(18, 36.5, 40),
                            centre.point = c(20, 1.4, 1.45),
                            positive.point = c(22, 1.54, 1.595),
                            negative.point = c(18, 1.26, 1.305))

## Prepare 3rd data.frame to be filled

reaction.quants <- as.data.frame(matrix(NA, dim(yates.1)[1], dim(yates.1)[2]))
names(reaction.quants) <- reaction.info[,2]

reaction.quants[yates.1[,1] == 1 ,1] <- round(5 * reaction.info[1,3] * reaction.info[1,5], 3)
reaction.quants[yates.1[,1] == -1 ,1] <- round(5 * reaction.info[1,3] * reaction.info[1,6], 3)

reaction.quants[yates.1[,2] == 1 ,2] <- round(5 * reaction.info[2,3] * reaction.info[2,5], 3)
reaction.quants[yates.1[,2] == -1 ,2] <- round(5 * reaction.info[2,3] * reaction.info[2,6], 3)

reaction.quants[yates.1[,3] == 1 ,3] <- round(5 * reaction.info[3,3] * reaction.info[3,5], 3)
reaction.quants[yates.1[,3] == -1 ,3] <- round(5 * reaction.info[3,3] * reaction.info[3,6], 3)

## three data.frames

yates
reaction.info
reaction.quants

Upvotes: 1

Views: 30

Answers (1)

Parfait
Parfait

Reputation: 107687

Consider refactoring with ifelse logic and filtered vectors using a user defined method since logic is very similar but across different columns:

convert_col <- function(nm) {
  with(reaction.info,
       ifelse(reaction.quants_new[[nm]] == 1, 
              round(5 * Mw[Component==nm] * positive.point[Component==nm], 3),
              round(5 * Mw[Component==nm] * negative.point[Component==nm], 3)
       )
  )
}

# INITIALIZE DATA FRAME (CAN BE NESTED IN NEXT CALL)
reaction.quants_new <- setNames(data.frame(yates.1), reaction.info$Component)

# ADD COLUMNS (CAN ALSO USE TRANSFORM)
reaction.quants_new <- within(reaction.quants_new, {
    Water <- convert_col("Water")
    SM <- convert_col("SM")
    Reagent <- convert_col("Reagent")
})

reaction.quants_new
#   Water     SM Reagent
# 1  1980 281.05     319
# 2  1620 281.05     319
# 3  1980 229.95     319
# 4  1620 229.95     319
# 5  1980 281.05     261
# 6  1620 281.05     261
# 7  1980 229.95     261
# 8  1620 229.95     261

identical(reaction.quants, reaction.quants_new)
# [1] TRUE

And with pipes available in R 4.1+ still using user-defined method:

reaction.quants_new <- data.frame(yates.1) |>
     setNames(reaction.info$Component) |>
     within({
         Water <- convert_col("Water")
         SM <- convert_col("SM")
         Reagent <- convert_col("Reagent")
     })

Upvotes: 1

Related Questions