60 votes

Comment arrondir un data.frame en R qui contient des variables de caractère?

Je ai un dataframe et je souhaite arrondir tous les nombres (prêts pour l'exportation). Cela doit être simple, mais je rencontre des problèmes parce que certaines parties du dataframe ne sont pas des chiffres numériques. Par exemple, je veux arrondir les chiffres au chiffre entier le plus proche dans l'exemple ci-dessous:

ID = c("a","b","c","d","e")
Value1 = c("3.4","6.4","8.7","1.1","0.1")
Value2 = c("8.2","1.7","6.4","1.9","10.3")
df<-data.frame(ID,Value1,Value2)

Est-ce que quelqu'un peut m'aider? Je peux arrondir des colonnes individuelles (par exemple, round(df$Value1, 2)) mais je veux arrondir un tableau entier qui contient certaines colonnes qui ne sont pas numériques.

1 votes

L'arrondi a du sens pour les "nombres", pas pour les caractères. Vous devrez convertir Value1 et Value2 en numérique, par exemple round(as.numeric(Value1), 0) devrait faire l'affaire, mais vous n'avez pas spécifié comment l'arrondi devrait être effectué (recherchez l'un de troncature, plafond ou plancher).

4voto

Ben Points 8166

Les autres réponses ne répondent pas tout à fait exactement à la question de l'OP car elles supposent que les données d'exemple sont différentes de celles fournies par l'OP.

Si nous lisons littéralement la question et que nous voulons une solution générale qui trouvera les colonnes contenant des chiffres (de n'importe quel type de vecteur), les convertira en numérique, puis effectuera une autre opération numérique, telle que l'arrondi. Nous pouvons utiliser purrr:dmap et le faire ainsi :

Voici les données telles que fournies par l'OP, où toutes les colonnes sont des facteurs (une valeur par défaut ennuyeuse, mais nous pouvons y remédier) :

ID = c("a","b","c","d","e")
Value1 = c("3.4","6.4","8.7","1.1","0.1")
Value2 = c("8.2","1.7","6.4","1.9","10.3")
df<-data.frame(ID,Value1,Value2)

str(df)
'data.frame':   5 obs. of  3 variables:
 $ ID    : Factor w/ 5 levels "a","b","c","d",..: 1 2 3 4 5
 $ Value1: Factor w/ 5 levels "0.1","1.1","3.4",..: 3 4 5 2 1
 $ Value2: Factor w/ 5 levels "1.7","1.9","10.3",..: 5 1 4 2 3

Nous rechercherons les colonnes contenant des chiffres et créerons un dataframe d'indices pour marquer les numériques :

library(dplyr)
library(purrr)

df_logical <- 
df %>% 
  dmap(function(i) grepl("[0-9]", i))

df_logical
     ID Value1 Value2
1 FALSE   TRUE   TRUE
2 FALSE   TRUE   TRUE
3 FALSE   TRUE   TRUE
4 FALSE   TRUE   TRUE
5 FALSE   TRUE   TRUE

str(df_logical)
'data.frame':   5 obs. of  3 variables:
 $ ID    : logi  FALSE FALSE FALSE FALSE FALSE
 $ Value1: logi  TRUE TRUE TRUE TRUE TRUE
 $ Value2: logi  TRUE TRUE TRUE TRUE TRUE

Ensuite, nous pouvons utiliser ces indices pour sélectionner un sous-ensemble des colonnes du dataframe d'origine, les convertir en numérique, et faire d'autres choses également (dans ce cas, l'arrondi) :

df_numerics <- 
map(1:ncol(df), function(i) ifelse(df_logical[,i], 
                                      as.numeric(as.character(df[,i])), 
                                      df[,i])) %>% 
  dmap(round, 0) %>% 
  setNames(names(df)) 

Et nous avons le résultat souhaité :

df_numerics
  ID Value1 Value2
1  1      3      8
2  2      6      2
3  3      9      6
4  4      1      2
5  5      0     10

str(df_numerics)
'data.frame':   5 obs. of  3 variables:
 $ ID    : num  1 2 3 4 5
 $ Value1: num  3 6 9 1 0
 $ Value2: num  8 2 6 2 10

Ceci pourrait être utile dans le cas d'un dataframe avec un grand nombre de colonnes, et où nous avons de nombreuses colonnes de type caractère/facteur remplies de chiffres que nous voulons en numérique, mais qu'il est trop fastidieux de le faire manuellement.

2voto

Matt L. Points 992

Les réponses ci-dessus soulignent quelques obstacles dans la question initiale qui la rendent plus compliquée que simplement arrondir plusieurs colonnes, principalement :

  1. Les chiffres ont été saisi en tant que caractères, et
  2. data.frame() convertit par défaut les caractères-chiffres en facteurs

La réponse de Ben explique comment gérer ces problèmes, et utilise purrr::dmap(). Le package purrr a depuis été modifié et la fonction dmap est obsolète (au profit de map_df()).
Il existe également une nouvelle fonction, modify_if() qui peut résoudre le problème d'arrondi de plusieurs colonnes numériques, c'est pourquoi j'ai souhaité mettre à jour cette réponse.


Je vais entrer les données sous forme de chiffres, en ajoutant quelques chiffres supplémentaires à arrondir pour rendre l'exemple plus largement applicable :

df <- data.frame(ID = c("a","b","c","d","e"), 
                 Value1 =c(3.4532897,6.41325,8.71235,1.115,0.115), 
                 Value2 = c(8.2125,1.71235,6.4135,1.915,10.3235))

Utilisation de la fonction purrr::modify_if():

purrr::modify_if(df, ~is.numeric(.), ~round(., 0))

  ID Value1 Value2
1  a      3      8
2  b      6      2
3  c      9      6
4  d      1      2
5  e      0     10

il suffit de changer round(digits= 0) pour le nombre de décimales approprié

modify_if(df, ~is.numeric(.), ~round(., 2))
  ID Value1 Value2
1  a   3.45   8.21
2  b   6.41   1.71
3  c   8.71   6.41
4  d   1.12   1.92
5  e   0.12  10.32

voir http://purrr.tidyverse.org/ pour plus de documentation sur la syntaxe

Cela pourrait également être fait en deux étapes en utilisant les fonctions apply de base de R, en créant un index pour les colonnes (numVars) puis en utilisant l'indexation standard pour modifier uniquement ces colonnes :

numVars <-  sapply(df, is.numeric)
   ID Value1 Value2 
FALSE   TRUE   TRUE 

df[, numVars] <- lapply(df[, numVars], round, 0)
df
  ID Value1 Value2
1  a      3      8
2  b      6      2
3  c      9      6
4  d      1      2
5  e      0     10

0 votes

Notez que numVars <- apply(df, 2, is.numeric) échoue, car il coerce le dataframe en une matrice (convertit toutes les colonnes en le même type - caractère). sapply() ne fait pas cela.

0 votes

Modifier si ne semble pas exister dans le package purrr actuel. Je reçois des erreurs indiquant que modify_if n'est pas trouvé après avoir téléchargé et chargé le package purrr

0 votes

@Mark hmmm...je viens de vérifier et c'est dans la version CRAN actuelle (purrr 0.2.3). J'ai légèrement modifié la commande donc c'est purrr::modify_if afin que vous n'ayez pas à charger le package. Voyez si cela vous aide.

2voto

Rtist Points 391

Notez que certaines solutions proposées ci-dessus ne prennent pas en compte les noms de ligne, ce qui signifie qu'ils sont perdus.

Par exemple, essayez :

df <- data.frame(v1 = seq(1.11, 1.20, 0.01), v2 = letters[1:10])
row.names(df) = df$v2

et ensuite, comme suggéré ci-dessus, essayez :

data.frame( lapply(df, function(y) if(is.numeric(y)) round(y, 2) else y) ) 

Notez que les noms de ligne ne sont plus là.

La suggestion d'Akhmed conserve les noms de ligne car elle fonctionne avec des remplacements.

2voto

sashahafner Points 61

Voici une alternative. Cette fonction facilite la spécification de la fonction de rounding réelle et accepte une valeur de chiffres unique pour chaque colonne :

rounddf <- function(x, digits = rep(2, ncol(x)), func = round) {
  if (length(digits) == 1) {
    digits <- rep(digits, ncol(x))
  } else if (length(digits) != ncol(x)) {
    digits <- c(digits, rep(digits[1], ncol(x) - length(digits)))
    warning('First value in digits repeated to match length.')
  }

  for(i in 1:ncol(x)) {
    if(class(x[, i])[1] == 'numeric') x[, i] <- func(x[, i], digits[i])
  }

  return(x)
}

Il est posté (et parfois mis à jour) sur https://github.com/sashahafner/jumbled

1voto

Gago-Silva Points 1845

Pourquoi ne pas simplement utiliser l'ID comme nom de ligne?

... et enlever les "'" des données de value1 et value2

Essayez ceci à la place:

ID = c("a","b","c","d","e")
Value1 = c(3.4,6.4,8.7,1.1,0.1)
Value2 = c(8.2,1.7,6.4,1.9,10.3)

df<-data.frame(ID,Value1,Value2,row.names=TRUE)

> df
  Value1 Value2
a    3.4    8.2
b    6.4    1.7
c    8.7    6.4
d    1.1    1.9
e    0.1   10.3

> str(df)
'data.frame':   5 obs. of  2 variables:
 $ Value1: num  3.4 6.4 8.7 1.1 0.1
 $ Value2: num  8.2 1.7 6.4 1.9 10.3

Je ne suis pas sûr de ce que vous voulez faire avec l'arrondi, mais vous avez quelques options en R:

?ceiling()
?floor()
?trunc()

2 votes

Dans le cas où vous savez quelles colonnes vous voulez arrondir et convertir, vous pouvez également faire df[,c('Value1','Value2')] <- round(as.numeric(df[,c('Value1','Value2')])) (cela peut être souhaitable s'il y a de nombreuses colonnes de texte mais seulement quelques-unes qui peuvent être rendues numériques).

6 votes

De plus, si vous souhaitez rechercher uniquement les colonnes numériques et arrondir, vous pourriez utiliser df[,sapply(df, is.numeric)] <-round(df[,sapply(df, is.numeric)],0)

0 votes

Merci Tyler! C'était exactement ce que je cherchais - tu m'as beaucoup sauvé de temps!!

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X