2.4 Convertion entre les types atomiques

Les conversions automatiques

Nous avons vu au chapitre précédent que un vecteur doit toujours être composé d'éléments du même type. Mais que ce passe-il si on essaye quand même de créer un vecteur comportant des types différents ? Et bien R va simplement simplement changer le type de certains éléments pour qu'au final tous les éléments partagent le même type ! C'est ce qu'on appelle une conversion de types.

Regardons un exemple de cette conversion automatique. Essayons de créer un vecteur qui contient à la fois un nombre double et un nombre complex .

mon_vecteur = c(3.14, 2+2i)
print(mon_vecteur)

On voit que notre vecteur a bien été crée, mais que le premier élément à été converti en complex . Tous les éléments sont donc bien au final du même type ! Nous allons voir dans ce chapitre comment R réalise ces conversions automatiques, ainsi que comment les faire manuellement si nous le souhaitons.

La régle d'or : le type le plus important l'emporte sur les autres

La régle principale utilisée pour convertir les éléments est plutôt simple : tous les éléments sont converti dans le type le plus haut classé présent dans le vecteur, les types étant classés selon cet ordre : character > complex > double > integer > logicial .

Regardons l'application de cette régle avec cet exemple :

vect1 = c(4L, 3.4, 2+4i, TRUE, FALSE) #tous les types sont présents dans ce vecteur
print(vect1)
#affiche 4.0+0i 3.4+0i 2.0+4i 1.0+0i 0.0+0i
typeof(vect1) #affiche "complex"

Le type le mieux classé parmis ceux présent dans le vecteur est le type complex . Tous les éléments du vecteur ont bien été converti en nombre complexes, selon la régle qui veut que le type le plus haut classé l'emporte.

Regardons maintenant un second exemple :

vect2 = c(1L, 3.14, 4+2i, TRUE, FALSE, NA, "Washington") 
print(vect2)
#affiche "1"          "3.14"       "4+2i"       "FALSE"      NA           "Washington"
typeof(vect2) #affiche "character"

Comme le type character est mieux classé que tous les autres types et que notre vecteur contient au moins une chaine, c'est ce type qui l'emporte. Notre vecteur va donc devenir un vecteur de character et R va convertir tous les autres éléments en character . On peut le voir a l'affiche du vecteur dans la console, ainsi qu'en affichait directement le type du vecteur avec l'instruction typeof(vect2) .

Entrons dans les détails

Maintenant que nous connaissons la régle principale, examinons plus en détail dans les régles "secondaires" qui indiquent comment les différents types sont convertis entre eux. Comment est converti un nombre en chaine de caractére par exemple ? Est-ce de 2 se transforme en "deux" ? Voici les régles exactes que R utilise :

  • Si tous les éléments sont des nombres (integer, double ou complex ) alors R convertit tous les éléments dans le type le plus "large".
  • Si le type le plus haut est character alors les nombres et booleans sont convertis en chaine. Les élements se voient ajouter des "" autour. Par exemple pour les nombres, 3 devient "3" , 2+4i devient "2+4i" . Pour les booleans, TRUE est convertie en "TRUE" et FALSE est converti en "FALSE" . NA reste lui toujours NA mais est maintenant un NA de la classe characteret non plus de la classe logical . On peut s'en appercevoir sur le bloc de code précédent. Nous expliquerons pourquoi les NA se comportent ainsi dans quelques paragraphes.
  • Si le vecteur contient uniquement des nombres et des logical , alors ces derniers sont convertis en nombre car les types de nombres sont supérieur au type logical . Comment on peut s'y attendre, TRUE est converti en 1 et FALSE en 0. En détail, TRUE devient ainsi 1L , 1 ou 1+0i selon le type dominant, et FALSE devient lui 0L,0 ou 0+0i . Là aussi, NA garde sa valeur NA mais change de type pour devenir un NA du type dominant.
  • Les valeurs NA restent NA mais changent de type pour prendre le type le mieux classé.

Illustrons ces régles avec quelques exemples pour mieux les apréhender.

Un vecteur avec uniquement des nombres :

Commençons par le premier cas, quand notre vecteur ne comporte que des nombres :

vect_nombres = c(1L, 3.14, 4+2i, NA)
print(vect_nombres) 
#affiche : 1.00+0i 3.14+0i 4.00+2i   NA
typeof(vect_nombres) #complex

Comme prévu, le type le mieux classé est le type complex , tous les éléments sont converti en nombres complexes. Remarquez que NA garde la même valeur, mais devient de type complex et abandonne son type de base qui est logical .

Un vecteur avec une chaine :

Nous avons vu cet exemple plus haut, revoyons le à nouveau :

vect2 = c(1L, 3.14, 4+2i, TRUE, FALSE, NA, "Washington") 
print(vect2)
#affiche "1"          "3.14"       "4+2i"       "FALSE"      NA           "Washington"
typeof(vect2) #affiche "character"

Les différentes valeurs se voient bien ajouter des guillements "" comme prévue par la seconde "sous régle". Là encore, NA ne bouge pas et change de type pour adopter le type dominant du vecteur.

Un vecteur avec des nombres et des boleans :

Regardons le dernier cas à l'oeuvre quand notre vecteur contient uniquement des nombres et des booléens, mais pas de chaines de caractére :

vect3 = c(1L, 3.14, 4+2i, TRUE, FALSE, NA)
print(vect3)
#affiche : 1.00+0i 3.14+0i 4.00+2i 1.00+0i 0.00+0i      NA
typeof(vect3) #affiche "complex"

On voit bien que tous les éléments ont été convertis en nombres, ici le type complexcar celui le plus large parmis ceux des éléments du vecteur.

Résumé des régles :

Les régles de conversion de type sont assez simples au final. Les chaines de caractéres l'emportent sur le reste, puis les nombres, qui eux même se convertissent dans le type le plus large parmis les trois types disponibles (integer, double ou complex ). Les logcial se convertissent toujours, soit en chaine, soit en nombre, sauf si le vecteur ne contient que des logical . Enfin, les NA gardent leurs valeur NA mais changent de type pour adopter le type dominant. Si vous retenez correctement l'ordre de priorité (character , complex , double , integer , logical ), vous ne devriez jamais avoir de mauvaise surprise en manipulant des objets de types différents.

Pourquoi les NA restent NA ?

Revenons un petit peu sur une particularité des changements de types : les valeurs NA se convertissent en changeant de type pour prendre le type le plus haut, mais gardent toujours la valeur NA . Pourquoi ?

NA sert à représenter les données manquantes comme nous l'avons vu au chapitre précédent. Quelque soit la nature des données, une donnée manquante reste manquante, et doit pouvoir être identifée en tant que telle. Si NA se transformait en "NA" quand le type dominant est character , alors on ne pourrait plus savoir si il s'agit réellement d'une donnée manquante ou de la chaine "NA" ,qui pourrait avoir une autre signification. De même, en quel nombre convertir NA dans un vecteur numérique ? Quel que soit le nombre qu'on choisirait, on pourrait le confondre avec une vraie observation.

Pour ces raisons, une valeur manquante est toujours représentée par la valeur NA , qui en contrepartie peut prendre tous les types disponibles. Il existe ainsi un NA avec le type character , mais aussi une version complex , double , integer et bien entendu logical , qui est le type par défaut de NA. De cette façon on peut toujours représenter nos données manquantes par la valeur NA , quel que soit le types de données que l'on manipule. Tout en respectant le principe qui veut qu'un vecteur contienne uniquement des éléments d'un même type.

Faire ses propres conversions de types

Nous avons vu les différents régles que R utilise quand on essaye de créer un vecteur avec des éléments qui n'ont pas le même type atomique. Mais il est également possible de faire des conversions manuellement entre les différents types. R fournit pour cela un certain nombres de fonctions qui vont nous permettre de choisir le type dans lequel convertir nos variables. Il peut être parfois trés utile de pouvoir choisir le type d'une variable, en modidant son type existant.

Il existe une fonction de conversion pour chaque type de données atomiques : integer , double ,complex , character et logical . Chaque fonction se présente sous la forme as.type() . Les 5 fonctions sont les suivantes :

  • as.integer()
  • as.double()
  • as.complex()
  • as.character()
  • as.logical()

En réalité quand R réalise une conversion automatique comme nous l'avons au dessus, il utilise ces fonctions là en "secret" pour modifier les types des éléments. Regardons en détail leur fonctionnement, en commencons par les trois fonctions qui portent sur les nombres (integer , double et complex).

Convertir un vecteur en nombre :

Chaque type de nombre dispose de sa propre fonction pour convertir des objets en nombres de leur type. Néanmoins ces fonctions ne marchent pas avec tous types d'objets comme nous allons le voir : elles fonctionnent bien avec les autres types de nombres et les boléens, mais mal avec les chaines de caractéres. Les trois fonctions de conversion des nombres sont celle-ci :

as.integer() #converti en integer
as.double() #converti en double
as.complex() #converti en nombre complexe

Convertir un nombre en nombre

Les nombres entre eux se convertissent naturellement. Convertir en un type "supérieur" fonctionne normalement, comme nous l'avons vu dans les chapitres précédents. Convertir dans l'autre sens est moins évident. Si vous essayer de convertir un complexe en un nombre de type "inférieur", alors la partie imaginaire du nombre sera partie. Seule la partie réelle sera conservée. De même si vous convertissez un nombre réel de type double et entier de type integer alors la partie réelle sera perdue.

#Convertir vers un type supérieur de pose pas de problème
as.double(5L) #donne 5.0
as.complex(3.14) #donne 3.14 + 0i
#Convertir d'un type supérieur vers un type inférieur engendre une perte d'information
as.integer(3.9) #donne 3
as.double(3 + 4i) #donne 3
as.double(3.14+4i) #donne 3, on perds la partie imaginaire et la partie décimale

Il faut donc toujours penser à cette perte d'information si vous voulez convertir un nombre en type de "inférieur".

Convertir un boléen en nombre

Les booléens se convertissent naturellement en nombre, comme nous avons vu plus haut à propos de la conversion automatique. Pour rappel, TRUE devient 1L , 1 ou 1+0i selon le type de nombre demandé, et FALSE se transforme lui 0L,0 ou 0+0i . NA lui change simplement de type, mais garde la même valeur.

#Les conversions de booléen en nombres sont plutôt évidentes
as.integer(TRUE) #donne 1L
as.double(TRUE) #donne 1
as.complex(FALSE) #donne 0 + 0i
as.complexe(NA) #donne NA de type complexe
typeof(as.complex(NA)) #affiche bien complex

Convertir une chaine en nombre

En général, il est impossible de convertir une chaine de caractére en nombre. Dans ce cas, la console affiche une erreur et la conversion revois la valeur NA , du type dans lequel vous avez tenté la conversion.

as.integer("Washington") #renvoit un NA de type integer
as.double("Washington") #renvoit un NA de type double 
as.complex("Washington") #renvoit un NA de type complex

La seule exception où la conversion d'une chaine est possible se produit est quand la chaine correspond à un nombre entouré de "" . R peut alors naturellement en extraire le nombre en question. Regardez le code suivant :

as.integer("54") #renvoit 54L 
as.double("3.14") #renvoit 3.14
as.complex("3+2i") #renvoit 3+2i
as.complex("3 + 2i") #ATTENTION ne fonctionne pas, car il y a des espaces dans la chaine

La fonction as.numeric() :

Il existe une 4éme fonction pour convertir les objets en nombre, qui est la fonction as.numeric() . Cette fonction est exactement identique à as.double() , mais possède simplement un nom différent. Elle provient des anciennes versions de R, à une époque où on utilisait une autre classification pour différencier les données et dans laquelle les nombres se divisent seulement entre nombres complexes et numeric (qui engloble les integer et les double).

age = as.numeric("25")
typeof(age) #affiche double

Cette fonction as.numeric() reste très utilisée. Vous risquez de la recontrer souvent au cours de votre apprentisage, c'est pourquoi nous vous la présentons ici. Il est également possible que nous l'utilisions aussi dans le cadre de ce cours, car elle à l'avantage d'avoir un non explicite que tout le monde comprends. En pratique, le type integer n'est quasiment jamais utilisé, la plupart des gens travaillent avec le type double et les fonctions assimilées comme as.numeric() .

R est un langage très riche, qui hérite de nombreuses fonctionalités passées qui peuvent parfois faire doublons avec les nouveautés du langage, comme ici as.numeric() qui existe encore alors que la classification des types de données dont ce nom était issue n'est plus d'actualité. cela peut sembler un peu déroutant pour le débutant, qui à l'impression qu'il existe plein de choses qui ont plus ou moins la même fonction mais des noms différents. Nous essayerons à chaque fois de nous présenter une façon d'écrire R moderne, tous en vous présentant de temps en temps ce qui était utilisé avant, pour que vous ne soyez pas perdu si un jour vous tomber sur du code un peu ancien.

Convertir un vecteur en chaine de caractére :

La conversion en chaine de caractéres se fait à l'aide de la fonction as.character() . Elle est toujours possible car character est le type de plus "élevé" dans la hiérarchie des types. Ainsi les nombres sont converti en ajoutant simplement des "" autour, les booléens TRUE et FALSEsont converti en "TRUE" et "FALSE" (même T et F sont converti ainsi). Les NA gardent comme toujours leur valeur même prennent le type character .

Regardons ceci avec quelques exemples :

vect1 = as.character(c(1L, 3.14, 4+4i, TRUE, FALSE, T, F, NA)) 
print(vect1)
#affiche : "1+0i"    "3.14+0i" "4+4i"    "1+0i"    "0+0i"    "1+0i"    "0+0i"    NA     
typeof(vect1) #character

Il n'y a donc jamais de problème pour convertir nos vecteurs atomiques en chaines.

Convertir un vecteur en logical :

Comme vous devez vous en douter, la fonction pour convertir un objet en logical s'appelle as.logical() . Cette fonction converti les nombres et les chaines de caractéres selon différents critéres.

Les nombres d'un vecteur qui valent 0 (cela comprend le zéro entier, double et complexe) sont converti en FALSE . Tous les autres nombres sont eux converti en TRUE .

vect2 = as.logical(c(0L, 2L, 0.0, 3.14, 0+0i, 0+1i, 5+5i))
 print(vect2)
 #affiche : FALSE  TRUE FALSE  TRUE FALSE  TRUE  TRUE
 typeof(vect2) #logical

Il faut bien que la partie imaginaire d'un nombre complexe soit nulle également pour qu'il soit considéré comme FALSE , attention !

Remarque : La seule expection concerne NaN : il sera toujours converti en NA au lieu de TRUE ou FALSE :

print(as.logical(NaN)) #affiche NA

Concernant les chaines de caractéres, la régle suivit par la fonction as.logical() est simple. Les chaines "TRUE" , "true"et "T" sont convertie en TRUE, quand "FALSE" , "false" et "F" sont converti en FALSE . Toute autre chaine, quelque soit sa valeur, est convertie en NA de type logical. Regardonc celà avec un exemple :

vect3 = as.logical(c("TRUE", "T", "FALSE", "F", "", "0", "Washington", "FALse", "TRUe")
print(vect3)
#affiche : TRUE  TRUE FALSE FALSE    NA    NA    NA   NA   NA
typeof(vect3) #logical

Ainsi contrairement à ce que l'on pourrait croire, une chaine vide "" ou encore "" sont bien convertie en NA, et non en FALSE comme on aurait peut-être pu s'y attendre. De même mélanger les majuscules et miniscules pour écrire TRUE ou FALSE donne un NA, seuls les versions entiérement en majuscules ou miniscules sont acceptéees comme valides.

Conclusion

Vous savez maintenant comme R converti automatiquement les différents types entre eux lors de la création d'un vecteur qui n'est pas uniforme. Cela est très important car il arrive souvent que l'on ai à manipuler des données de types différents, comprendre les régles de conversion implicites comme pouvoir choisir de changer le type des données est indispensable à tout useR qui se respect !

A retenir :

  • Quand on créer un vecteur avec des élèments de types différents, R va automatiquement les convertir en un seul type.
  • R convertis dans le type le plus haut classé selon cet ordre, si au moins un élément du vecteur est de ce type : character , complex , double , integer , logical .
  • Les NA peuvent changer de type mais ne changent jamais de valeur car ils représentent les données manquantes.
  • On peut convertir naturellement un vecteur dans un autre type avec les fonctions as.character() , as.complex() ,as.double() , as.integer() , as.numeric() (qui est équivalent à as.double() ) et as.logical() .

results matching ""

    No results matching ""