Comment formater un nombre en pourcentage dans R?


135

L'une des choses qui me rendait perplexe en tant que nouveau venu chez R était de savoir comment formater un nombre en pourcentage pour l'impression.

Par exemple, affichez 0.12345comme 12.345%. J'ai un certain nombre de solutions de contournement pour cela, mais aucune de celles-ci ne semble être "newby friendly". Par exemple:

set.seed(1)
m <- runif(5)

paste(round(100*m, 2), "%", sep="")
[1] "26.55%" "37.21%" "57.29%" "90.82%" "20.17%"

sprintf("%1.2f%%", 100*m)
[1] "26.55%" "37.21%" "57.29%" "90.82%" "20.17%"

Question: Existe - t-il une fonction de base R pour ce faire? Sinon, existe-t-il un package largement utilisé qui fournit un emballage pratique?


Malgré la recherche de quelque chose comme ça dans ?format, ?formatCet ?prettyNum, je dois encore trouver un emballage convenablement pratique dans la base R. ??"percent"n'a donné quelque chose d' utile. library(sos); findFn("format percent")renvoie 1250 hits - donc encore une fois pas utile. ggplot2a une fonction percentmais cela ne donne aucun contrôle sur la précision d'arrondi.


5
sprintfsemble être la solution préférée sur les listes de diffusion, et je n'ai pas vu de meilleure solution. Toute fonction intégrée ne sera pas beaucoup plus simple à appeler de toute façon, non?
michel-slm

1
À mon avis, cela sprintfconvient parfaitement à ce sous-ensemble de codeurs R qui sont également des programmeurs. J'ai beaucoup codé dans ma vie, y compris COBOL (frisson) et fortran (montre mon âge). Mais je ne considère pas les sprintfrègles de formatage comme évidentes (traduction: WTF?). Et bien sûr, un wrapper dédié doit être plus facile à appeler qu'un sprintf, par exemple:format_percent(x=0.12345, digits=2)
Andrie

@hircus Je pense que c'est assez courant pour qu'il mérite sa propre courte fonction curry. C'est particulièrement un problème avec Sweave, où \ Sexpr {sprintf (% 1.2f %% ", myvar)} est beaucoup plus laid que \ Sexpr {pct (myvar)} ou quelle que soit la fonction la plus courte.
Ari B. Friedman

2
Nous ne devrions pas nous attendre à ce que les utilisateurs s'efforcent d'apprendre à utiliser les outils appropriés? Je veux dire, apprendre à utiliser sprintf()ne prend guère plus de temps que de découvrir ce que contient le paquet fooformat_percent() . Que se passe-t-il si l'utilisateur ne souhaite pas formater en pourcentage mais quelque chose d'autre qui est similaire? Ils ont besoin de trouver un autre emballage. À long terme, l'apprentissage des outils de base sera bénéfique.
Gavin Simpson

1
Il y a un léger problème en ce que %c'est le caractère de commentaire dans LaTeX, qui est le format de rapport "par défaut" pour R. Donc, même s'il peut être utile pour étiqueter les graphiques, il faut faire attention si le nombre formaté doit être Sweaved.
James

Réponses:


118

Encore plus tard:

Comme l'a souligné @DzimitryM, percent()a été «retiré» en faveur de label_percent(), qui est un synonyme de l'ancienne percent_format()fonction.

label_percent() renvoie une fonction, donc pour l'utiliser, vous avez besoin d'une paire supplémentaire de parenthèses.

library(scales)
x <- c(-1, 0, 0.1, 0.555555, 1, 100)
label_percent()(x)
## [1] "-100%"   "0%"      "10%"     "56%"     "100%"    "10 000%"

Personnalisez-le en ajoutant des arguments dans le premier jeu de parenthèses.

label_percent(big.mark = ",", suffix = " percent")(x)
## [1] "-100 percent"   "0 percent"      "10 percent"    
## [4] "56 percent"     "100 percent"    "10,000 percent"

Une mise à jour, plusieurs années plus tard:

De nos jours, il y a une percentfonction dans le scalespackage, comme indiqué dans la réponse de krlmlr. Utilisez cela au lieu de ma solution roulée à la main.


Essayez quelque chose comme

percent <- function(x, digits = 2, format = "f", ...) {
  paste0(formatC(100 * x, format = format, digits = digits, ...), "%")
}

Avec l'utilisation, par exemple,

x <- c(-1, 0, 0.1, 0.555555, 1, 100)
percent(x)

(Si vous préférez, changez le format de "f"à "g".)


2
Oui, cela fonctionne et est une version légèrement plus générale de la solution de contournement que j'ai fournie dans la question. Mais ma vraie question est de savoir si cela existe dans la base R ou non.
Andrie

Fonctionne pour moi dans la liste des pourcentages, mais le remplacement de «x» par «pour cent (x)» dans une commande statistique ou graphique produit un message d'erreur.
rolando2

@ rolando2 Ma réponse et la réponse de krlmlr renvoient des vecteurs de caractères en sortie, pas des nombres. Ils servent à formater les étiquettes des axes et autres. Peut-être voulez-vous simplement multiplier par 100?
Richie Cotton

À partir de 2020 scalesver. Le manuel 1.1.0 indique: percent()est retiré; veuillez utiliser à la label_percent()place, ce qui ne convient pas au formatage des nombres . Pour que la solution roulée à la main soit toujours d'actualité
DzimitryM

74

Découvrez le scalespackage. Cela faisait partie de ggplot2, je pense.

library('scales')
percent((1:10) / 100)
#  [1] "1%"  "2%"  "3%"  "4%"  "5%"  "6%"  "7%"  "8%"  "9%"  "10%"

La logique intégrée de détection de la précision devrait fonctionner suffisamment bien dans la plupart des cas.

percent((1:10) / 1000)
#  [1] "0.1%" "0.2%" "0.3%" "0.4%" "0.5%" "0.6%" "0.7%" "0.8%" "0.9%" "1.0%"
percent((1:10) / 100000)
#  [1] "0.001%" "0.002%" "0.003%" "0.004%" "0.005%" "0.006%" "0.007%" "0.008%"
#  [9] "0.009%" "0.010%"
percent(sqrt(seq(0, 1, by=0.1)))
#  [1] "0%"   "32%"  "45%"  "55%"  "63%"  "71%"  "77%"  "84%"  "89%"  "95%" 
# [11] "100%"
percent(seq(0, 0.1, by=0.01) ** 2)
#  [1] "0.00%" "0.01%" "0.04%" "0.09%" "0.16%" "0.25%" "0.36%" "0.49%" "0.64%"
# [10] "0.81%" "1.00%"

2
Ne fonctionne pas pour les nombres négatifs. percent(-0.1)produitNaN%
akhmed

1
@akhmed: Cela a déjà été signalé, un correctif est disponible mais en attente d'examen: github.com/hadley/scales/issues/50 . Notez que cela semble fonctionner pour plus d'un nombre négatif:scales::percent(c(-0.1, -0.2))
krlmlr

Merci pour le lien! Je n'étais pas sûr s'il s'agissait d'une fonctionnalité ou d'un bug. Pour plusieurs numéros, cela fonctionne parfois et parfois non. Dites, scales::percent(c(-0.1,-0.1,-0.1))produit "NaN%" "NaN%" "NaN%"mais votre exemple fonctionne. Pour la référence des autres, le bogue n'est pas encore corrigé en date du scales_0.2.4. De plus, à ce jour, la demande de tirage correspondante la fixant n'est pas encore fusionnée dans la branche principale.
akhmed

34

Découvrez la percentfonction du formattablepackage:

library(formattable)
x <- c(0.23, 0.95, 0.3)
percent(x)
[1] 23.00% 95.00% 30.00%

4
+1, cela permet de spécifier le nombre de chiffres à inclure, ce qui scales::percentn'est pas le cas dans les deux premières réponses.
Sam Firke

3
+1, même s'il est assez facile de lancer votre propre fonction, permettre de choisir le nombre de chiffres est vraiment utile.
Gang Su

10

J'ai fait quelques comparaisons de vitesse sur ces réponses et j'ai été surpris de voir percentdans le scalespaquet si vanté, compte tenu de sa lenteur. J'imagine que l'avantage est son détecteur automatique pour un formatage correct, mais si vous savez à quoi ressemblent vos données, il semble clair à éviter.

Voici les résultats de la tentative de mise en forme d'une liste de 100 000 pourcentages en (0,1) en un pourcentage à 2 chiffres:

library(microbenchmark)
x = runif(1e5)
microbenchmark(times = 100L, andrie1(), andrie2(), richie(), krlmlr())
# Unit: milliseconds
#   expr       min        lq      mean    median        uq       max
# 1 andrie1()  91.08811  95.51952  99.54368  97.39548 102.75665 126.54918 #paste(round())
# 2 andrie2()  43.75678  45.56284  49.20919  47.42042  51.23483  69.10444 #sprintf()
# 3  richie()  79.35606  82.30379  87.29905  84.47743  90.38425 112.22889 #paste(formatC())
# 4  krlmlr() 243.19699 267.74435 304.16202 280.28878 311.41978 534.55904 #scales::percent()

Cela sprintfressort clairement comme un gagnant lorsque nous voulons ajouter un signe de pourcentage. Par contre, si nous voulons seulement multiplier le nombre et arrondir (passer de proportion en pourcentage sans "%", alors round()c'est le plus rapide:

# Unit: milliseconds
#        expr      min        lq      mean    median        uq       max
# 1 andrie1()  4.43576  4.514349  4.583014  4.547911  4.640199  4.939159 # round()
# 2 andrie2() 42.26545 42.462963 43.229595 42.960719 43.642912 47.344517 # sprintf()
# 3  richie() 64.99420 65.872592 67.480730 66.731730 67.950658 96.722691 # formatC()

8

Vous pouvez utiliser le package scale juste pour cette opération (sans le charger avec require ou une bibliothèque)

scales::percent(m)

1
Comment donner la précision du nombre de chiffres?
Elmex80s

6

Voici ma solution pour définir une nouvelle fonction (principalement pour que je puisse jouer avec Curry et Compose :-)):

library(roxygen)
printpct <- Compose(function(x) x*100, Curry(sprintf,fmt="%1.2f%%"))

3

Voyant comment il scalable::percentavait déjà été démontré que c'était le plus lent et que Liliana Pacheco proposait une autre solution, je suis allé de l'avant et j'ai essayé de la comparer à certaines des autres options basées sur l'exemple de Michael:

library(microbenchmark)
library(scales)
library(formattable)

x<-runif(1e5)

lilip <- function() formattable::percent(x,2)
krlmlr <- function() scales::percent(x)
andrie1 <- function() paste0(round(x,4) * 100, '%')

microbenchmark(times=100L,lilip(), krlmlr(), andrie1())

Voici les résultats que j'ai obtenus:

Unit: microseconds
      expr        min          lq        mean      median          uq        max neval
   lilip()    194.562    373.7335    772.5663    889.7045    950.4035   1611.537   100
  krlmlr() 226270.845 237985.6560 260194.9269 251581.0235 280704.2320 373022.180   100
 andrie1()  87916.021  90437.4820  92791.8923  92636.8420  94448.7040 102543.252   100

Je n'ai aucune idée, cependant, pourquoi mon krlmlr()et a andrie1()exécuté tellement pire que dans l'exemple de MichaelChirico. Des indices?


0
try this~

data_format <- function(data,digit=2,type='%'){
if(type=='d') {
    type = 'f';
    digit = 0;
}
switch(type,
    '%' = {format <- paste("%.", digit, "f%", type, sep='');num <- 100},
    'f' = {format <- paste("%.", digit, type, sep='');num <- 1},
    cat(type, "is not a recognized type\n")
)
sprintf(format, num * data)
}

0

Cette fonction pourrait transformer les données en pourcentages par colonnes

percent.colmns = function(base, columnas = 1:ncol(base), filas = 1:nrow(base)){
    base2 = base
    for(j in columnas){
        suma.c = sum(base[,j])
        for(i in filas){
            base2[i,j] = base[i,j]*100/suma.c
        }
    }
    return(base2)
}

L'arithmétique de base est vectorisée - la boucle for interne est inefficace et inutile. Peut être remplacé par base2[, j] = base[ , j] * 100 / suma.c. Il convient également de noter que ce n'est pas exactement une réponse à la question ... la question porte sur le formatage de quelque chose comme 0.5"50,0%", pas sur le calcul ...
Gregor Thomas

0

La tidyverseversion est la suivante:

> library(tidyverse)

> set.seed(1)
> m <- runif(5)
> dt <- as.data.frame(m)

> dt %>% mutate(perc=scales::percent(m,accuracy=0.001))
          m    perc
1 0.2655087 26.551%
2 0.3721239 37.212%
3 0.5728534 57.285%
4 0.9082078 90.821%
5 0.2016819 20.168%

Il a l'air bien rangé comme d'habitude.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.