Home > Software engineering >  Sum and find average of all the value's in a data frame row based upon one of the data frame�
Sum and find average of all the value's in a data frame row based upon one of the data frame�

Time:01-13

I have code that creates my desired output; however, it is painfully slow. I have two input data sets(metaClustering_perCell, data_clean). Each row index of data_clean corresponds to the index position of metaClustering_per cell. here is an example of the two data sets.

dput(head(data_clean[1:5],10))

structure(
  list(
    `NA` = c(1, 2, 3, 4, 5, 6, 7, 8, 9, 10),
    EGFP.A = c(326, 314, 341, 0, 198, 295, 325, 309, 400, 328),
    CD43.PE.A = c(435, 402, 469, 283, 303, 371, 442, 363, 444, 358),
    CD45.PE.Vio770.A = c(399, 385, 379, 438, 384, 331, 402, 392, 354, 430),
    CD235a_41a.APC.A = c(412, 618, 239, 562, 661, 193, 363, 385, 408, 265),
    APC.Vio770.A = c(447, 491, 444, 437, 477, 328, 453, 326, 353, 0)
  ),
  row.names = c(NA, -10L),
  class = "data.frame"
)
NA EGFP.A CD43.PE.A CD45.PE.Vio770.A CD235a_41a.APC.A APC.Vio770.A
1 326 435 399 412 447
2 314 402 385 618 491
3 341 469 379 239 444
4 0 283 438 562 437
5 198 303 384 661 477
6 295 371 331 193 328
7 325 442 402 363 453
8 309 363 392 385 326
9 400 444 354 408 353
10 328 358 430 265 0
dput(head(metaClustering_perCell,10))

c("1 Population", "1 Population", "1 Population", "1 Population", "1 Population",
"1 Population", "1 Population", "1 Population", "1 Population", "9 Population")

I wish to make a heatmap ultimately with the average values of the markers (EGFP.A, CD43.PE.A.....) but, my data sets will contain almost 2e8 cells that are sorted into a predetermined number of populations. The code that I wrote is shown here that creates 2 empty dataframes. The df_sum stores the running summation of the markers (EGFP.A, CD43.PE.A.....) while df_count takes a running tally of the total events in each population. Ultimately the code then takes the average by dividing the dataframe by the vector. The code is here.

# create an empty matrix
df_sum  <- data.frame(matrix(ncol = length(data_clean), nrow = num_clusters))
pops_header <- unique(metaClustering_perCell)
rownames(df_sum) <- pops_header
colnames(df_sum) <- colnames(data_clean)

# creates empty table for storing the count values
df_count <- data.frame(matrix(ncol = num_clusters, nrow = 1))
colnames(df_count) <- pops_header



df[is.na(df_sum)] <- 0
df_count[is.na(df_count)] <- 0



for (i in 1:length(metaClustering_perCell)){

  # only takes one row at a time of original data
  volt_vals <- data_clean[i,]
  
  # find the column to place it in (population)
  pop <- metaClustering_perCell[i]
  
  # Tally for each population
  df_count[1,pop] <- df_count[1,pop]   1
  
  # adds to the previous value in the dataframe
  for (a in colnames(volt_vals)){
    df_sum[pop, a] <- volt_vals[a]   df_sum[pop, a]
  }
    
  # creates another dataframe same size as df to overwrite with the averages
  df_aves <- df_sum
  
  
  # Divide the df_=
  for (n in pops_header){
    df_aves[n,] <- mapply('/', df_sum[n,], df_count[n])
  }
}

The output that I get is this (I clipped them off to make is easier to see)

>head(df_sum[1:3],10)
NA EGFP.A CD43.PE.A CD45.PE.Vio770.A
1 Population 26062897 35936578 32784372.
9 Population 1045468 1591084 1576716.
2 Population 4374137 8673145 6555053.
8 Population 818413 44836 1318176.
5 Population 217605 443341 439357.
6 Population 1056157 1558711 43206.
7 Population 747037 883763 1134664.
3 Population 1561994 2376586 2329772.
4 Population 54940 9346 137085.
10 Population 172735 213079 8043.
>head(df_count[1:5])
Population 9 Population 2 Population 8 Population 5 Population
78909 4262 12982 4447 1392
> head(df_aves[1:3], 10)
NA EGFP.A CD43.PE.A CD45.PE.Vio770.A
1 Population 330.2905 455.41799 415.470631
9 Population 245.2999 373.31863 369.947443
2 Population 336.9386 668.09005 504.933986
8 Population 184.0371 10.08230 296.419159
5 Population 156.3254 318.49210 315.630029
6 Population 235.1195 346.99711 9.618433
7 Population 186.1079 220.17015 282.676632
3 Population 256.1906 389.79597 382.117763
4 Population 160.1749 27.24781 399.664723
10 Population 201.5578 248.63361 9.385064

The data frame of averages of each population and their values for each of the column headers(markers) is exactly what I want..... however, it is brutally slow.... and I mean brutal. This is my first week with R (I come knowing self taught python from the stacks), so please explain thoroughly. Thanks for the help.

CodePudding user response:

It's unclear exactly what you're trying to achieve, and the sample data is too sparse to help disambiguate, but here are my two guesses:

Averages Of Each Marker Within Each Population

This interpretation is most consistent with your sample output, in which each population (cluster) appears only once, as if the data were aggregated by population.

It is very straightforward in R to group data and then summarize it with aggregate functions.

Solution 1.1: dplyr

Here's a solution with the dplyr package, which is syntactically intuitive:

library(dplyr)

data_clean %>%
  # Overwrite the 'NA' column with the cluster labels.
  mutate(`NA` = metaClustering_perCell) %>%
  # Group by cluster labels...
  group_by(`NA`) %>%
  # ...and summarize the average of each marker (column).
  summarize(across(everything(), mean))

Solution 1.2: data.table

Here's a solution with data.table, which offers even better performance.

library(data.table)

as.data.table(data_clean)[,
  # Overwrite the 'NA' column with the cluster labels.
  ("NA") := metaClustering_perCell
][,
  # Summarize the average of each marker (column), as grouped by cluster.
  lapply(.SD, mean), by = `NA`
]

Result

Let the values for data_clean and metaClustering_perCell be as sampled in your question.

While the first result (1.1) will be a tibble and the second (1.2) a data.table, each will contain the following data:

          NA   EGFP.A CD43.PE.A CD45.PE.Vio770.A CD235a_41a.APC.A APC.Vio770.A
1 Population 278.6667  390.2222         384.8889         426.7778     417.3333
9 Population 328.0000  358.0000         430.0000         265.0000       0.0000

Cumulative Averages ("") As Of Each Observation

This interpretation is most consistent with your algorithm, which seems to calculate its metrics (average, etc.) on a running basis, for each observation (row).

R also facilitates cumulative averages, sums, and so forth. It is far more efficient to leverage vectorized operations than to compute these metrics iteratively (with loops, the *apply() family, etc.) for each row.

Solution 2.1: dplyr

Serendipitously, dplyr already has its own cummean() function.

library(dplyr)

data_clean %>%
  # Overwrite the 'NA' column with the cluster labels.
  mutate(`NA` = metaClustering_perCell) %>%
  # Group by cluster labels...
  group_by(`NA`) %>%
  # ...and overwrite each marker (column) with its running average.
  mutate(across(everything(), cummean)) %>% ungroup()

Solution 2.2: data.table

With data.table we can improvise our own (anonymous) function

function(x) {
  cumsum(x) / seq_along(x)
}

which divides the running sum by the running count, to calculate the cumulative mean along a vector (column). We could also import dplyr and use cummean in place of our function.

library(data.table)

as.data.table(data_clean)[,
  # Overwrite the 'NA' column with the cluster labels.
  ("NA") := metaClustering_perCell
][,
  # Overwrite each marker (column) with its running average, as grouped by cluster.
  lapply(.SD, function(x)cumsum(x)/seq_along(x)), by = `NA`
]

Result

Let the values for data_clean and metaClustering_perCell be as sampled in your question.

While the first result (1.1) will be a tibble and the second (1.2) a data.table, each will contain the following data:

          NA   EGFP.A CD43.PE.A CD45.PE.Vio770.A CD235a_41a.APC.A APC.Vio770.A
1 Population 326.0000  435.0000         399.0000         412.0000     447.0000
1 Population 320.0000  418.5000         392.0000         515.0000     469.0000
1 Population 327.0000  435.3333         387.6667         423.0000     460.6667
1 Population 245.2500  397.2500         400.2500         457.7500     454.7500
1 Population 235.8000  378.4000         397.0000         498.4000     459.2000
1 Population 245.6667  377.1667         386.0000         447.5000     437.3333
1 Population 257.0000  386.4286         388.2857         435.4286     439.5714
1 Population 263.5000  383.5000         388.7500         429.1250     425.3750
1 Population 278.6667  390.2222         384.8889         426.7778     417.3333
9 Population 328.0000  358.0000         430.0000         265.0000       0.0000
  •  Tags:  
  • Related