1
votes

Comment écrire une fonction pour couper de nombreuses colonnes dans R en fonction d'un jeu de données de contrôle

J'ai une base de données sur les expositions aux produits chimiques qui ressemble à ceci:

    Error in cut.default(., chem_dat$.x, breaks = c(-Inf, quantile(control_chem_dat$.x,  : 
  'x' must be numeric
  

Je dois convertir les valeurs continues de chacun des produits chimiques en catégories basées sur la valeur d'exposition. La distribution des valeurs est très biaisée, avec de nombreuses valeurs 0 et quelques valeurs très élevées. Ces coupes doivent être basées sur le sous-ensemble de l'ensemble de données qui contient des contrôles, qui ressemble à celui ci-dessus. Le résultat devrait ressembler à ceci:

chem_cut <- map(chem_names, ~ chem_dat %>%
                       cut(  chem_dat$.x, 
                       breaks=c(-Inf, quantile(control_chem_dat$.x , probs=c(0.5,0.75), na.rm=TRUE), Inf)) %>% 
                       mutate(!!str_c(.x, "cut", sep = "_") := .x))

J'ai utilisé la fonction de coupe comme ceci pour chaque produit chimique individuel:

chem_dat$chem_1 <- cut(chem_dat$chem_1 , breaks=c(-Inf, quantile(control_chem_dat$chem_1 , probs=c( 0.5,0.75), na.rm=TRUE), Inf)) 

Je veux pouvoir faire ceci, ou quelque chose comme ça, à toutes les colonnes à l'intérieur d'une fonction afin de ne pas avoir à écrire ce que j'ai plus de 524 fois. Je ne peux pas utiliser la fonction ntile car elle pose des problèmes avec des colonnes extrêmement 0 gonflées.

Voici ce que j'ai essayé:

chem_names XXX

Cela me donne l'erreur

      chem1_cut      chem2_cut     chem3_cut ...
      (-inf, 0.1]  (0.1, 12.1]  (0.1, 12.1]       
      (0.1, 12.1]  (12.1, inf]     NA      
      (0.1, 12.1]  (0.1, 12.1]  (-inf, 0.1]      

Comment puis-je corriger cette fonction pour faire ce que je veux? Ou, y a-t-il une meilleure façon de faire cette tâche avec une sorte de package ou quelque chose?

Merci pour votre aide!


2 commentaires

chem_dat et control_chem_dat ne sont pas les mêmes data.frames, non? si tel est le cas, veuillez fournir un exemple de control_chem_dat


Oui, control_chem_dat est un data.frame différent avec exactement le même format. Merci pour cette suggestion.


3 Réponses :


1
votes

Essayez cette solution base R . On considère que chem_dat et control_chem_dat sont des dataframes séparés. Dans cet exemple, j'ai défini les mêmes valeurs mais vous pouvez les remplacer. En espérant que cela peut vous aider:

#Data
chem_dat <- structure(list(chem1 = c(0.06, 0.7, 0.4), chem2 = c(6.8, 24.3,2.9),
                    chem3 = c(0.3, NA, 0.03), chem524 = c(0.2, 0.7, 1.6)),
               class = "data.frame", row.names = c(NA,-3L))
#Data
control_chem_dat <- structure(list(chem1 = c(0.06, 0.7, 0.4), chem2 = c(6.8, 24.3,2.9),
                    chem3 = c(0.3, NA, 0.03), chem524 = c(0.2, 0.7, 1.6)),
               class = "data.frame", row.names = c(NA,-3L))
#Function
cut_func <- function(x,y)
{
  z <- cut(y,breaks=c(-Inf, quantile(x , probs=c( 0.5,0.75), na.rm=TRUE), Inf))
  return(z)
}
#Apply
Result <- as.data.frame(mapply(cut_func,control_chem_dat,chem_dat))

        chem1       chem2        chem3     chem524
1  (-Inf,0.4]  (-Inf,6.8] (0.232, Inf]  (-Inf,0.7]
2 (0.55, Inf] (15.6, Inf]         <NA>  (-Inf,0.7]
3  (-Inf,0.4]  (-Inf,6.8] (-Inf,0.165] (1.15, Inf]


4 commentaires

C'est une bonne solution, mais je ne sais pas si les ruptures doivent être basées sur les quantiles d'un autre df control_chem_dat


@starja il prend les quantiles en fonction des valeurs de chaque colonne. Il n'y a pas d'autre dataframe, juste celui que vous avez.


C'était aussi ma première pensée, mais dans le code de @Justin Andrew, il utilise en fait les quantiles de control_chem_dat pour les ruptures de données dans chem_dat


@starja Dans ce cas, la deuxième trame de données doit être fournie. Mais je comprends que vous ne devez utiliser qu'un seul dataframe.



1
votes

Vous pouvez utiliser lapply pour générer l'index de colonne et appliquer la fonction à chaque colonne de votre chem_dat . L'utilisation de l'index présente l'avantage de pouvoir également indexer control_chem_dat (étant donné que les colonnes sont dans le même ordre). Cela génère une liste avec une entrée pour chaque colonne, vous pouvez utiliser cbind pour le lier à un data.frame:

chem_cut_list <- lapply(seq_len(ncol(chem_dat)), 2, function(i)  {
  cut(chem_dat[, i] , breaks=c(-Inf, quantile(control_chem_dat[, i],
                                              probs=c( 0.5,0.75), na.rm=TRUE), Inf))
})

chem_cut <- do.call("cbind", chem_cut_list)


0 commentaires

2
votes

Dans purrr , il y a une fonction map2 * pour parcourir plusieurs arguments simultanément. Lorsqu'un data.frame est fourni à map * () , il parcourra les colonnes. Essayons-le avec un exemple de jeu de données:

map2_dfc(control_chem_dat,chem_dat,cut_y_by_x)
# A tibble: 5 x 3
  chem1         chem2        chem3       
  <fct>         <fct>        <fct>       
1 (0.453, Inf]  (-Inf,0.27]  (0.432, Inf]
2 (0.403,0.453] (0.351, Inf] (-Inf,0.383]
3 (0.453, Inf]  (0.351, Inf] (0.432, Inf]
4 (0.403,0.453] (0.27,0.351] (0.432, Inf]
5 (0.453, Inf]  (-Inf,0.27]  (-Inf,0.383]

Écrivez une fonction pour effectuer cette tâche, étant donné x , coupez y comme vous l'avez:

mapply(cut_y_by_x,control_chem_dat,chem_dat)

Dans la base R, nous faisons ceci (pour que vous puissiez voir le parallèle dans purrr):

cut_y_by_x = function(x,y){
   cut(y,c(-Inf, quantile(x , probs=c(0.5,0.75), na.rm=TRUE),+Inf))
}

Faisons ceci dans purrr:

library(purrr)
set.seed(555)

control_chem_dat = data.frame(matrix(runif(10*3,min=0,max=0.5),ncol=3))
colnames(control_chem_dat) = paste0("chem",1:3)

chem_dat = data.frame(matrix(runif(5*3,min=0,max=1),ncol=3))
colnames(chem_dat) = paste0("chem",1:3)


0 commentaires