Comment remplacer les valeurs NA par des zéros dans une trame de données R?


Réponses:


880

Voir mon commentaire dans la réponse @ gsk3. Un exemple simple:

> m <- matrix(sample(c(NA, 1:10), 100, replace = TRUE), 10)
> d <- as.data.frame(m)
   V1 V2 V3 V4 V5 V6 V7 V8 V9 V10
1   4  3 NA  3  7  6  6 10  6   5
2   9  8  9  5 10 NA  2  1  7   2
3   1  1  6  3  6 NA  1  4  1   6
4  NA  4 NA  7 10  2 NA  4  1   8
5   1  2  4 NA  2  6  2  6  7   4
6  NA  3 NA NA 10  2  1 10  8   4
7   4  4  9 10  9  8  9  4 10  NA
8   5  8  3  2  1  4  5  9  4   7
9   3  9 10  1  9  9 10  5  3   3
10  4  2  2  5 NA  9  7  2  5   5

> d[is.na(d)] <- 0

> d
   V1 V2 V3 V4 V5 V6 V7 V8 V9 V10
1   4  3  0  3  7  6  6 10  6   5
2   9  8  9  5 10  0  2  1  7   2
3   1  1  6  3  6  0  1  4  1   6
4   0  4  0  7 10  2  0  4  1   8
5   1  2  4  0  2  6  2  6  7   4
6   0  3  0  0 10  2  1 10  8   4
7   4  4  9 10  9  8  9  4 10   0
8   5  8  3  2  1  4  5  9  4   7
9   3  9 10  1  9  9 10  5  3   3
10  4  2  2  5  0  9  7  2  5   5

Il n'est pas nécessaire de postuler apply. =)

ÉDITER

Vous devriez également jeter un œil au normpackage. Il a beaucoup de fonctionnalités intéressantes pour l'analyse des données manquantes. =)


2
J'ai déjà essayé ce code hier avant de le poster et je n'ai pas travaillé. Parce que j'ai posté la question. Mais j'ai essayé de savoir et de travailler parfaitement. Je pense que je faisais quelque chose de mal.
Renato Dinhani

12
@ RenatoDinhaniConceição: si vous avez déjà essayé quelque chose, il est utile de partager ces informations lorsque vous posez la question; il aide à préciser où le problème peut être.
Aaron a quitté Stack Overflow

2
d [is.na (d)] <- 0 n'a pas de sens pour moi. Cela semble à l'envers? Comment R traite-t-il cette déclaration?
user798719

13
@ user798719 - "<-" est l'opérateur d'affectation de R, et peut être lu comme: faites quelque chose sur le côté droit puis affectez-le à l'emplacement / nom sur la gauche. Dans ce cas, nous ne «faisons» vraiment rien - nous ne faisons que des zéros. Le côté gauche dit: regardez l'objet d, à l'intérieur de l'objet d (les crochets), trouvez tous les éléments qui retournent VRAI (is.na (d) retourne une logique pour chaque élément). Une fois qu'ils sont trouvés, remplacez-les ("attribuez-les") par la valeur 0. Cela laisse tous les non-NA tels qu'ils étaient et ne remplace que ceux qui manquent.
Twitch_City

3
Et ... si vous avez une trame de données et que vous souhaitez uniquement appliquer le remplacement à des vecteurs nurmériques spécifiques (en laissant par exemple ... des chaînes avec NA):df[19:28][is.na(df[19:28])] <- 0
jtdoud

299

Les options hybrides dplyr sont désormais environ 30% plus rapides que le sous-ensemble Base R réaffecté. Sur un point de données de 100 M, la trame de données mutate_all(~replace(., is.na(.), 0))s'exécute une demi-seconde plus rapidement que l' d[is.na(d)] <- 0option R de base . Ce que l'on veut éviter spécifiquement, c'est utiliser un ifelse()ou un if_else(). (L'analyse complète de 600 essais a duré plus de 4,5 heures, principalement en raison de l'inclusion de ces approches.) Veuillez consulter les analyses de référence ci-dessous pour les résultats complets.

Si vous rencontrez des difficultés avec des trames de données massives, data.tablec'est l'option la plus rapide de toutes: 40% plus rapide que l' approche Base R standard . Il modifie également les données en place, ce qui vous permet de travailler avec presque deux fois plus de données à la fois.


Un regroupement d'autres approches de remplacement bidirectionnelles utiles

Localisation:

  • indice mutate_at(c(5:10), ~replace(., is.na(.), 0))
  • référence directe mutate_at(vars(var5:var10), ~replace(., is.na(.), 0))
  • match fixe mutate_at(vars(contains("1")), ~replace(., is.na(.), 0))
    • ou à la place de contains(), essayez ends_with(),starts_with()
  • correspondance de motif mutate_at(vars(matches("\\d{2}")), ~replace(., is.na(.), 0))

Conditionnellement:
(changez juste un seul type et laissez les autres types seuls.)

  • entiers mutate_if(is.integer, ~replace(., is.na(.), 0))
  • Nombres mutate_if(is.numeric, ~replace(., is.na(.), 0))
  • cordes mutate_if(is.character, ~replace(., is.na(.), 0))

L'analyse complète -

Mise à jour pour dplyr 0.8.0: les fonctions utilisent des ~symboles au format purrr : remplacement des funs()arguments obsolètes .

Approches testées:

# Base R: 
baseR.sbst.rssgn   <- function(x) { x[is.na(x)] <- 0; x }
baseR.replace      <- function(x) { replace(x, is.na(x), 0) }
baseR.for          <- function(x) { for(j in 1:ncol(x))
    x[[j]][is.na(x[[j]])] = 0 }

# tidyverse
## dplyr
dplyr_if_else      <- function(x) { mutate_all(x, ~if_else(is.na(.), 0, .)) }
dplyr_coalesce     <- function(x) { mutate_all(x, ~coalesce(., 0)) }

## tidyr
tidyr_replace_na   <- function(x) { replace_na(x, as.list(setNames(rep(0, 10), as.list(c(paste0("var", 1:10)))))) }

## hybrid 
hybrd.ifelse     <- function(x) { mutate_all(x, ~ifelse(is.na(.), 0, .)) }
hybrd.replace_na <- function(x) { mutate_all(x, ~replace_na(., 0)) }
hybrd.replace    <- function(x) { mutate_all(x, ~replace(., is.na(.), 0)) }
hybrd.rplc_at.idx<- function(x) { mutate_at(x, c(1:10), ~replace(., is.na(.), 0)) }
hybrd.rplc_at.nse<- function(x) { mutate_at(x, vars(var1:var10), ~replace(., is.na(.), 0)) }
hybrd.rplc_at.stw<- function(x) { mutate_at(x, vars(starts_with("var")), ~replace(., is.na(.), 0)) }
hybrd.rplc_at.ctn<- function(x) { mutate_at(x, vars(contains("var")), ~replace(., is.na(.), 0)) }
hybrd.rplc_at.mtc<- function(x) { mutate_at(x, vars(matches("\\d+")), ~replace(., is.na(.), 0)) }
hybrd.rplc_if    <- function(x) { mutate_if(x, is.numeric, ~replace(., is.na(.), 0)) }

# data.table   
library(data.table)
DT.for.set.nms   <- function(x) { for (j in names(x))
    set(x,which(is.na(x[[j]])),j,0) }
DT.for.set.sqln  <- function(x) { for (j in seq_len(ncol(x)))
    set(x,which(is.na(x[[j]])),j,0) }
DT.nafill        <- function(x) { nafill(df, fill=0)}
DT.setnafill     <- function(x) { setnafill(df, fill=0)}

Le code de cette analyse:

library(microbenchmark)
# 20% NA filled dataframe of 10 Million rows and 10 columns
set.seed(42) # to recreate the exact dataframe
dfN <- as.data.frame(matrix(sample(c(NA, as.numeric(1:4)), 1e7*10, replace = TRUE),
                            dimnames = list(NULL, paste0("var", 1:10)), 
                            ncol = 10))
# Running 600 trials with each replacement method 
# (the functions are excecuted locally - so that the original dataframe remains unmodified in all cases)
perf_results <- microbenchmark(
    hybrid.ifelse    = hybrid.ifelse(copy(dfN)),
    dplyr_if_else    = dplyr_if_else(copy(dfN)),
    hybrd.replace_na = hybrd.replace_na(copy(dfN)),
    baseR.sbst.rssgn = baseR.sbst.rssgn(copy(dfN)),
    baseR.replace    = baseR.replace(copy(dfN)),
    dplyr_coalesce   = dplyr_coalesce(copy(dfN)),
    tidyr_replace_na = tidyr_replace_na(copy(dfN)),
    hybrd.replace    = hybrd.replace(copy(dfN)),
    hybrd.rplc_at.ctn= hybrd.rplc_at.ctn(copy(dfN)),
    hybrd.rplc_at.nse= hybrd.rplc_at.nse(copy(dfN)),
    baseR.for        = baseR.for(copy(dfN)),
    hybrd.rplc_at.idx= hybrd.rplc_at.idx(copy(dfN)),
    DT.for.set.nms   = DT.for.set.nms(copy(dfN)),
    DT.for.set.sqln  = DT.for.set.sqln(copy(dfN)),
    times = 600L
)

Résumé des résultats

> print(perf_results)
Unit: milliseconds
              expr       min        lq     mean   median       uq      max neval
      hybrd.ifelse 6171.0439 6339.7046 6425.221 6407.397 6496.992 7052.851   600
     dplyr_if_else 3737.4954 3877.0983 3953.857 3946.024 4023.301 4539.428   600
  hybrd.replace_na 1497.8653 1706.1119 1748.464 1745.282 1789.804 2127.166   600
  baseR.sbst.rssgn 1480.5098 1686.1581 1730.006 1728.477 1772.951 2010.215   600
     baseR.replace 1457.4016 1681.5583 1725.481 1722.069 1766.916 2089.627   600
    dplyr_coalesce 1227.6150 1483.3520 1524.245 1519.454 1561.488 1996.859   600
  tidyr_replace_na 1248.3292 1473.1707 1521.889 1520.108 1570.382 1995.768   600
     hybrd.replace  913.1865 1197.3133 1233.336 1238.747 1276.141 1438.646   600
 hybrd.rplc_at.ctn  916.9339 1192.9885 1224.733 1227.628 1268.644 1466.085   600
 hybrd.rplc_at.nse  919.0270 1191.0541 1228.749 1228.635 1275.103 2882.040   600
         baseR.for  869.3169 1180.8311 1216.958 1224.407 1264.737 1459.726   600
 hybrd.rplc_at.idx  839.8915 1189.7465 1223.326 1228.329 1266.375 1565.794   600
    DT.for.set.nms  761.6086  915.8166 1015.457 1001.772 1106.315 1363.044   600
   DT.for.set.sqln  787.3535  918.8733 1017.812 1002.042 1122.474 1321.860   600

Boxplot of Results

ggplot(perf_results, aes(x=expr, y=time/10^9)) +
    geom_boxplot() +
    xlab('Expression') +
    ylab('Elapsed Time (Seconds)') +
    scale_y_continuous(breaks = seq(0,7,1)) +
    coord_flip()

Comparaison boxplot du temps écoulé

Diagramme de dispersion des essais à code couleur (avec axe y sur une échelle logarithmique)

qplot(y=time/10^9, data=perf_results, colour=expr) + 
    labs(y = "log10 Scaled Elapsed Time per Trial (secs)", x = "Trial Number") +
    coord_cartesian(ylim = c(0.75, 7.5)) +
    scale_y_log10(breaks=c(0.75, 0.875, 1, 1.25, 1.5, 1.75, seq(2, 7.5)))

Diagramme de dispersion de tous les temps d'essai

Une note sur les autres performants

Lorsque les ensembles de données deviennent plus grands, Tidyr 's replace_nas'était historiquement retiré devant. Avec la collection actuelle de 100 millions de points de données à parcourir, il fonctionne presque exactement ainsi qu'une base R pour boucle. Je suis curieux de voir ce qui se passe pour les trames de données de différentes tailles.

Des exemples supplémentaires pour les variantes de fonction mutateet summarize _atet _allpeuvent être trouvés ici: https://rdrr.io/cran/dplyr/man/summarise_all.html De plus, j'ai trouvé des démonstrations utiles et des collections d'exemples ici: https: //blog.exploratory. io / dplyr-0-5-is-awesome-heres-why-be095fd4eb8a

Attributions et appréciations

Un merci spécial à:

  • Tyler Rinker et Akrun pour avoir fait la démonstration de la référence.
  • alexis_laz pour m'avoir aidé à comprendre l'utilisation de local()(et avec l'aide du patient de Frank aussi) le rôle de la coercition silencieuse dans l'accélération de bon nombre de ces approches.
  • ArthurYip pour le poke d'ajouter la nouvelle coalesce()fonction et de mettre à jour l'analyse.
  • Gregor pour le coup de pouce pour comprendre les data.tablefonctions assez bien pour enfin les inclure dans la programmation.
  • Base R Pour boucle: alexis_laz
  • data.table pour les boucles: Matt_Dowle
  • Roman pour avoir expliqué ce is.numeric()qui teste vraiment.

(Bien sûr, veuillez nous contacter et leur donner également des votes positifs si vous trouvez ces approches utiles.)

Remarque sur mon utilisation de Numerics: Si vous disposez d'un ensemble de données purement entier, toutes vos fonctions s'exécuteront plus rapidement. Veuillez consulter le travail de alexiz_laz pour plus d'informations. IRL, je ne me souviens pas avoir rencontré un ensemble de données contenant plus de 10 à 15% d'entiers, donc j'exécute ces tests sur des trames de données entièrement numériques.

Matériel utilisé 3,9 GHz CPU avec 24 Go de RAM


2
@Frank - Merci d'avoir trouvé cet écart. Les références sont toutes nettoyées et les résultats ont été entièrement relus sur une seule machine et republiés.
leerssej

OK merci. En outre, je pense que df1[j][is.na(df1[j])] = 0c'est faux, devrait l'êtredf1[[j]][is.na(df1[[j]])] = 0
Frank

Oh maintenant je vois que vous l'avez écrit deux fois, différemment dans chaque benchmark. Quoi qu'il en soit, forLp_Sbstcela ne semble pas être un moyen pour quiconque d'envisager de l'approcher vsforLp_smplfSbst
Frank

1
@UweBlock - grande question: cela m'a permis de faire l'opération d'assignation gauche de sous-ensemble avec toutes les fonctions travaillant exactement sur la même trame de données. Comme je devais envelopper le local autour de cette fonction, alors au nom de la science [Un travail, vous aviez un travail!] Je l'ai enroulé autour de chacun d'eux afin que le terrain de jeu soit sans équivoque. Pour plus d'informations - veuillez voir ici: stackoverflow.com/questions/41604711/… J'avais réduit la réponse précédente plutôt longue - mais cette partie de la discussion serait bien à ajouter. Merci!
leerssej

1
@ArthurYip - J'ai ajouté l' coalesce()option et réexécuté tout le temps. Merci pour le coup de pouce à mettre à jour.
leerssej

128

Pour un seul vecteur:

x <- c(1,2,NA,4,5)
x[is.na(x)] <- 0

Pour un data.frame, créez une fonction à partir de ce qui précède, puis ajoutez- applyla aux colonnes.

Veuillez fournir un exemple reproductible la prochaine fois comme détaillé ici:

Comment faire un excellent exemple reproductible R?


18
is.naest une fonction générique et possède des méthodes pour les objets de data.frameclasse. donc celui-ci fonctionnera également sur data.frames!
aL3xa

3
Quand j'ai couru methods(is.na)pour la première fois, j'étais comme whaaa?!? . J'adore quand des trucs comme ça arrivent! =)
aL3xa

9
Supposons que vous ayez un bloc de données nommé df au lieu d'un seul vecteur et que vous souhaitiez simplement remplacer les observations manquantes dans une seule colonne nommée X3. Vous pouvez le faire avec cette ligne: df $ X3 [is.na (df $ X3)] <- 0
Mark Miller

8
Supposons que vous souhaitiez uniquement remplacer NA par 0 dans les colonnes 4 à 6 d'un bloc de données nommé my.df. Vous pouvez utiliser: my.df [, 4: 6] [is.na (my.df [, 4: 6])] <- 0
Mark Miller

comment se fait-il que vous passiez 'x' à is.na (x) existe-t-il un moyen de savoir quelles routines de bibliothèque dans R sont vectorisées?
uh_big_mike_boi

73

exemple dplyr:

library(dplyr)

df1 <- df1 %>%
    mutate(myCol1 = if_else(is.na(myCol1), 0, myCol1))

Note: Cela fonctionne par colonne sélectionnée, si nous devons le faire pour toutes les colonnes, voir @reidjax réponse de l' utilisation mutate_each .


57

Si nous essayons de remplacer NAs lors de l'exportation, par exemple lors de l'écriture vers csv, nous pouvons utiliser:

  write.csv(data, "data.csv", na = "0")

47

Je sais que la question est déjà répondue, mais le faire de cette façon pourrait être plus utile pour certains:

Définissez cette fonction:

na.zero <- function (x) {
    x[is.na(x)] <- 0
    return(x)
}

Maintenant, chaque fois que vous avez besoin de convertir les NA d'un vecteur en zéro, vous pouvez le faire:

na.zero(some.vector)

22

Avec dplyr0.5.0, vous pouvez utiliser une coalescefonction qui peut être facilement intégrée dans le %>%pipeline en faisant coalesce(vec, 0). Cela remplace toutes les NA vecavec 0:

Disons que nous avons une trame de données avec NAs:

library(dplyr)
df <- data.frame(v = c(1, 2, 3, NA, 5, 6, 8))

df
#    v
# 1  1
# 2  2
# 3  3
# 4 NA
# 5  5
# 6  6
# 7  8

df %>% mutate(v = coalesce(v, 0))
#   v
# 1 1
# 2 2
# 3 3
# 4 0
# 5 5
# 6 6
# 7 8

J'ai testé coalesce et il fonctionne à peu près comme remplacer. la commande coalesce est la plus simple à ce jour!
Arthur Yip

il serait utile de présenter comment l'appliquer sur toutes les colonnes de 2+ colonnes tibble.
jangorecki

21

Une approche plus générale de l' utilisation replace()dans la matrice ou d'un vecteur pour remplacer NAà0

Par exemple:

> x <- c(1,2,NA,NA,1,1)
> x1 <- replace(x,is.na(x),0)
> x1
[1] 1 2 0 0 1 1

C'est aussi une alternative à l'utilisation ifelse()dansdplyr

df = data.frame(col = c(1,2,NA,NA,1,1))
df <- df %>%
   mutate(col = replace(col,is.na(col),0))

1
Ma colonne était un facteur, j'ai donc dû ajouter ma valeur de remplacementlevels(A$x) <- append(levels(A$x), "notAnswered") A$x <- replace(A$x,which(is.na(A$x)),"notAnswered")
Climbs_lika_Spyder

1
whichn'est pas nécessaire ici, vous pouvez utiliser x1 <- replace(x,is.na(x),1).
lmo

J'ai essayé plusieurs façons proposées dans ce fil pour remplacer NAà 0en une seule colonne spécifique dans une grande trame de données et cette fonction a replace()travaillé le plus efficacement tout aussi le plus simplement.
Duc

19

Il est également possible d'utiliser tidyr::replace_na.

    library(tidyr)
    df <- df %>% mutate_all(funs(replace_na(.,0)))

9

Un autre exemple utilisant le paquet imputeTS :

library(imputeTS)
na.replace(yourDataframe, 0)

9

Si vous souhaitez remplacer les NA dans les variables de facteur, cela peut être utile:

n <- length(levels(data.vector))+1

data.vector <- as.numeric(data.vector)
data.vector[is.na(data.vector)] <- n
data.vector <- as.factor(data.vector)
levels(data.vector) <- c("level1","level2",...,"leveln", "NAlevel") 

Il transforme un facteur-vecteur en vecteur numérique et ajoute un autre niveau de facteur numérique artificiel, qui est ensuite retransformé en un vecteur-facteur avec un "niveau NA" supplémentaire de votre choix.


8

Aurait commenté le post de @ ianmunoz mais je n'ai pas assez de réputation. Vous pouvez combiner dplyr« s mutate_eachetreplace de prendre soin de NAde 0remplacement. Utilisation de la trame de données de la réponse de @ aL3xa ...

> m <- matrix(sample(c(NA, 1:10), 100, replace = TRUE), 10)
> d <- as.data.frame(m)
> d

    V1 V2 V3 V4 V5 V6 V7 V8 V9 V10
1   4  8  1  9  6  9 NA  8  9   8
2   8  3  6  8  2  1 NA NA  6   3
3   6  6  3 NA  2 NA NA  5  7   7
4  10  6  1  1  7  9  1 10  3  10
5  10  6  7 10 10  3  2  5  4   6
6   2  4  1  5  7 NA NA  8  4   4
7   7  2  3  1  4 10 NA  8  7   7
8   9  5  8 10  5  3  5  8  3   2
9   9  1  8  7  6  5 NA NA  6   7
10  6 10  8  7  1  1  2  2  5   7

> d %>% mutate_each( funs_( interp( ~replace(., is.na(.),0) ) ) )

    V1 V2 V3 V4 V5 V6 V7 V8 V9 V10
1   4  8  1  9  6  9  0  8  9   8
2   8  3  6  8  2  1  0  0  6   3
3   6  6  3  0  2  0  0  5  7   7
4  10  6  1  1  7  9  1 10  3  10
5  10  6  7 10 10  3  2  5  4   6
6   2  4  1  5  7  0  0  8  4   4
7   7  2  3  1  4 10  0  8  7   7
8   9  5  8 10  5  3  5  8  3   2
9   9  1  8  7  6  5  0  0  6   7
10  6 10  8  7  1  1  2  2  5   7

Nous utilisons l'évaluation standard (SE) ici, c'est pourquoi nous avons besoin du trait de soulignement sur " funs_." Nous utilisons également lazyeval's interp/ ~et les .références "tout ce avec quoi nous travaillons", c'est-à-dire la trame de données. Maintenant, il y a des zéros!


4

Vous pouvez utiliser replace()

Par exemple:

> x <- c(-1,0,1,0,NA,0,1,1)
> x1 <- replace(x,5,1)
> x1
[1] -1  0  1  0  1  0  1  1

> x1 <- replace(x,5,mean(x,na.rm=T))
> x1
[1] -1.00  0.00  1.00  0.00  0.29  0.00 1.00  1.00

6
Vrai, mais pratique uniquement lorsque vous connaissez l'indice de NAs dans votre vecteur. C'est bien pour les petits vecteurs comme dans votre exemple.
dardisco

4
@dardisco x1 <- replace(x,is.na(x),1)fonctionnera sans lister explicitement les valeurs d'index.
lmo

4

Une autre dplyroption compatible avec les tuyaux avec une tidyrméthode replace_naqui fonctionne pour plusieurs colonnes:

require(dplyr)
require(tidyr)

m <- matrix(sample(c(NA, 1:10), 100, replace = TRUE), 10)
d <- as.data.frame(m)

myList <- setNames(lapply(vector("list", ncol(d)), function(x) x <- 0), names(d))

df <- d %>% replace_na(myList)

Vous pouvez facilement limiter par exemple aux colonnes numériques:

d$str <- c("string", NA)

myList <- myList[sapply(d, is.numeric)]

df <- d %>% replace_na(myList)

4

La fonction dédiée ( nafill/ setnafill) à cet effet est dans la data.tableversion récente

install.packages("data.table", repos="https://Rdatatable.gitlab.io/data.table")
library(data.table)
ans_df = nafill(df, fill=0)
setnafill(df, fill=0) # this one updates in-place

Pour ceux qui votent, veuillez également fournir des commentaires, afin que ma réponse puisse être améliorée.
jangorecki

3

Cette simple fonction extraite de Datacamp pourrait aider:

replace_missings <- function(x, replacement) {
  is_miss <- is.na(x)
  x[is_miss] <- replacement

  message(sum(is_miss), " missings replaced by the value ", replacement)
  x
}

alors

replace_missings(df, replacement = 0)

3

Un moyen simple de l'écrire est if_nade hablar:

library(dplyr)
library(hablar)

df <- tibble(a = c(1, 2, 3, NA, 5, 6, 8))

df %>% 
  mutate(a = if_na(a, 0))

qui renvoie:

      a
  <dbl>
1     1
2     2
3     3
4     0
5     5
6     6
7     8

2

Pour remplacer toutes les AN dans une trame de données, vous pouvez utiliser:

df %>% replace(is.na(.), 0)


ce n'est pas une nouvelle solution
jogo

1

si vous souhaitez attribuer un nouveau nom après avoir modifié les NA dans une colonne spécifique dans ce cas, la colonne V3, utilisez vous pouvez également faire comme ceci

my.data.frame$the.new.column.name <- ifelse(is.na(my.data.frame$V3),0,1)
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.