Explorer et visualiser des données Big Data ou mégadonnées

en langage R

L'essentiel de cette page

Visualiser de grands jeux de données (ce qu'on appelle du Big-Data), sans perdre de l'information, est impossible.

Il existe toutefois des représentations pour visualiser de gros jeux de données, de taille intermédiaire, sans perdre d'information. On pourrait parler de la théorie des graphes (les représentations en réseau) développé dans une autre page. On s'intéressera ici au représentation pour visualiser l'ensemble d'un tableau de données, d'une dataframe d'un seul coup d’œil.

1- Visualiser les courbes de densité de toutes les variables d'un seul coup avec ridge_plot()

La fonction maison ridge_plot() permet d'afficher une courbe de densité pour chaque variable. Elle est efficace pour visualiser ainsi toutes les données d'une dataframe (d'un tableau) de 10 à 15 variables.

Elle permet aussi de subdiviser les coubes de densité par classes. On peut aussi donner plusieurs variables de classes pour croiser le tout.

Exemple avec les données wine de FactoMineR

# Les données

install.packages("FactoMineR") ; library("FactoMineR") ; data("wine")

# L'affichage

ridge_plot(wine[,1:12],by=wine[,1])


# Variables 1 à 12 en découpant les classes en fonction de la variable discrète 1

Chaque bâtonnet représente 1 individu.

Code de la fonction ridge_plot() à copier-coller (cliquer pour dérouler)

install.packages("RColorBrewer") ; install.packages("berryFunctions")

##############################

# ridge_plot() - version 02 - 08/06/2020

##############################

ridge_plot = function(data,by=c(),col_pch="#AAAAAA99",cmap="Set1") {

  # by : liste de classes ou dataframe de plusieurs classes à croiser.

  data <- data.frame(data)

  indices = c()

 for (i in c(1:ncol(data))) {

  if (is(data[,i])[1]=="numeric") {

   indices <- c(indices,i)

  }

 }

 if (length(indices) < ncol(data)) {cat("Attention, les variables non-numériques ne sont pas affichées !\n")}

 data <- data.frame(data[,indices])

 nvar = ncol(data)

 #layout(matrix(1:nvar,nvar,1))

 # Si la variable de catégorie n'est pas précisée

 if (length(by) == 0) {

  #plot.new()

  #par(mar=c(marge basse, marge gauche, marge haute, marge droite)) 

  for (i in c(1:nvar)) {

   density <- density(data[,i]) # créer une liste des densités

   if (i==1) {plot.new()}

   fig_temp <- c(0,1,(nvar-i)*(1/nvar),(nvar+1-i)*(1/nvar))

   par(fig=fig_temp, mar = c(0.2,0.2,0.5,0.2), new=TRUE)

   plot(x=data[,i],y=rep(max(density$y)/2,length(data[,i])),pch="|",col=col_pch,axes=F,main=colnames(data)[i],xlab="",ylab="",ylim=c(0,max(density$y)),cex.main=0.6)

   lines(density, col = "red",lwd=1) # Superposer la ligne

   polygon(density, col = "#AA111177") # Superposer le polygone

  }

# Si la/les variables de catégorie est précisée

 } else {

# Agglomérer les variables de catégories

  by = data.frame(by)

  #print(by)

  cat_all <- by[,1]

       if (ncol(by) > 1) {

   for (i in c(2:ncol(by))) {

    cat_all <- paste(cat_all,by[,i]) 

   }

  }

  cat_unique = unique(cat_all)

  require(RColorBrewer) ; require(berryFunctions)

  col = addAlpha(brewer.pal(n = length(cat_unique), name = cmap),0.5)

  for (i in c(1:nvar)) {

   #cat("Variable :",i,"\n")

   # Détection de la zone de densité maximale pour pondération de l'affichage

   maxx = 0

   for (j in c(1:length(cat_unique))) {

    x_temp = data[,i][cat_all==cat_unique[j]]

    maxx <- max(maxx,max(density(x_temp)$y))

   }

   if (i==1) {plot.new()}

   fig_temp <- c(0,1,(nvar-i)*(1/nvar),(nvar+1-i)*(1/nvar))

   par(fig=fig_temp, mar = c(0.2,0.2,0.5,0.2), new=TRUE)

   plot(x=data[,i],y=rep(max(density(data[,i])$y)/2,length(data[,i])),pch="|",col="white",axes=F,xlab="",main=colnames(data)[i],ylab="",ylim=c(0,maxx),cex.main=0.6)

   for (j in c(1:length(cat_unique))) {

    x_temp = data[,i][cat_all==cat_unique[j]]

    #cat(x_temp,"\n")

    densit <- density(x_temp) # créer une liste des densités

    if (i == 1) {

     #cat("stop1\n") 

     points(x=x_temp,y=rep(max/2,length(x_temp)),pch="|",col=col[j],cex=2)

    }else{

     #cat("stop2")

     points(x=x_temp,y=rep(maxx/2,length(x_temp)),pch="|",col=col[j],cex=2)}

    #cat("stop\n");#browser()

    lines(densit, col = col[j],lwd=1) # Superposer la ligne

    #cat("stop polygon\n");# browser()

    polygon(densit, col = col[j]) # Superposer le polygone

   }

  }

 }

}

En projet d'amélioration de cette fonction de kernel density 1D :

2- Heatmap, visualiser un tableau de données de façon interactive

matrixplot() permet de visualiser les données d'un tableau, les données manquantes en rouge et de trier toutes les données en fonction d'une variable en cliquant dessus.

# install.packages("VIM")

# Charger le jeu de données sleep (exemple)

data(sleep)

library("VIM")

# Sous RStudio (optionnel sous R)

x11() # Ouvrir fenêtre interactive

# Afficher la matrice

matrixplot(sleep,cex.axis=0.6)

# Cliquer dessus pour trier

# Faire échap à la fin

3- Coordonnées parallèles pour visualiser la relation entre une variables et plusieurs autres

Les coordonnées parallèles permettent de visualiser les données d'une dataframe en fonction d'une variable en particulier pour voir si cette variable est reliée à d'autres.

Dans cet exemple : la largeur des sépales d'iris est reliée à 3 autres variables. On voit ainsi que les sépales peu larges correspondent à des pétales peu larges aussi.

Le code pour réaliser ce graphique de parallele coordinate plot avec R

data(iris) ; colnames(iris)

library(plotly)

# Mettre titre d'une colonne en variable

j= "Sepal.Width"

iris[[j]]

# parallele coordinate plot sur variable

mon_titre = paste("Variable :",j)

fig <- iris %>% plot_ly(type = 'parcoords',

  line = list(color = ~get(j),

  colorscale = 'Jet',

  showscale = TRUE,

  reversescale = TRUE,

  cmin = min( iris[[j]]),

  cmax = max( iris[[j]])),

 dimensions = list(

  list(range = range(iris$Sepal.Length),

   label = 'Longueur Sépale', values = ~Sepal.Length),

  list(range = range(iris$Petal.Length),

   label = 'Longueur Pétale', values = ~Petal.Length),

  list(range = range(iris$Petal.Width),

   label = 'Largeur Pétale', values = ~Petal.Width)

      )) %>% layout(title = mon_titre)

#

print(fig)

Un exemple où l'on colore selon une catégorie (variable discrète)).

Ici on colore les données de chaque espèce d'iris.

Coloration de catégories en parcoords avec plotly (code à copier-coller)

# parallele coordinate plot en fonction d'une catégorie

# Conversion des catégories en valeurs comprises entre 0 et 1

categories <- as.character(iris$Species) ; j <- 0

for (i in unique(categories)) {

  categories[categories==i] <- j

 j <- j+0.5

}

categories <- as.numeric(categories)

# Graphique

mon_titre = paste("Espèces d'Iris")

fig <- iris %>% plot_ly(type = 'parcoords',

 line = list(color = categories,

             colorscale = list(c(0,'red'),c(0.5,'green'),c(1,'blue'))),

 dimensions = list(

  list(range = range(iris$Sepal.Length),

   label = 'Longueur Sépale', values = ~Sepal.Length),

  list(range = range(iris$Sepal.Width),

   label = "Largeur Sépale", values = ~Sepal.Width),

  list(range = range(iris$Petal.Length),

   label = 'Longueur Pétale', values = ~Petal.Length),

  list(range = range(iris$Petal.Width),

   label = 'Largeur Pétale', values = ~Petal.Width)

          )) %>% layout(title = mon_titre)


print(fig)

Fonction moins performante, mais idéal pour voir les données manquantes et comprendre comment elles sont reliées d'une variable à l'autre :

Coordonnées parallèles et données manquantes :

library("VIM")

data(sleep) ; colnames(sleep)

parcoordMiss(sleep[,1:7])

4-Treemap pour explorer des données qualitatives

Simulons d'abord un jeu de données qualitatives.

data(iris)

colnames(iris)

iris2 <- data.frame(

"A"=cut(iris[,1],pretty(range(iris[,1]),2)),

"B"=cut(iris[,2],pretty(range(iris[,2]),2)),

"C"=cut(iris[,3],pretty(range(iris[,3]),2)),

"D"=cut(iris[,4],pretty(range(iris[,4]),2)),

"Species"=iris$Species)

levels(iris2$A) <- c("SepLen petit","SepLen moyen","SepLen grand") ; levels(iris2$B) <- c("SepWid petit","SepWid moyen","SepWid grand") ; levels(iris2$C) <- c("PetLen petit","PetLen moyen","PetLen grand") ; levels(iris2$D) <- c("PetWit petit","PetWit moyen","PetWit grand")

table(iris2) -> table2 ; ftable(table2) -> table3 ; table3 <- data.frame(table3)

table3 <- table3[table3$Freq!=0,]

head(table3)

On a ainsi 5 variables qualitatives, il serait bien d'explorer combien j'ai d'iris de chaque espèce. Combien ont des grands pétales...

               A            B            C            D    Species Freq

1   SepLen petit SepWid petit PetLen petit PetWit petit     setosa    8

4   SepLen petit SepWid moyen PetLen petit PetWit petit     setosa   39

7   SepLen petit SepWid grand PetLen petit PetWit petit     setosa    3

82  SepLen petit SepWid petit PetLen petit PetWit petit versicolor    6

109 SepLen petit SepWid petit PetLen petit PetWit moyen versicolor   20

110 SepLen moyen SepWid petit PetLen petit PetWit moyen versicolor   14

On va ainsi créer un tableau de comptage ci-après qui compte les individus pour chaque catégorie et sous-catégorie :


                      ids       labels    parents Freq

1                  setosa       setosa              50

2              versicolor   versicolor              50

3               virginica    virginica              50

4     setosa-SepLen petit SepLen petit     setosa   50

5 versicolor-SepLen petit SepLen petit versicolor   30

6  virginica-SepLen petit SepLen petit  virginica    9

Comptabilisons les individus pour chaque catégorie et sous catégories


# Ordre de la prise en compte des variables

mydata <- iris2

ordre <- c(5,1:4)

ids <- c()

labels <- c()

Freq <- c()

parents <- c()


for (i in 1:length(ordre)) {

if (i==1) {

temp <- table(mydata[,ordre[i]])

ids <- c(ids,names(temp))

labels <- as.character(c(labels,names(temp)))

Freq <- c(Freq,table(mydata[,ordre[i]]))

parents <- as.character(c(parents,rep("",length(names(temp)))))

} else {

temp <- data.frame(ftable(table(iris2[,ordre[1:i]])))

temp <- temp[temp$Freq!=0,]

ids= c(ids,apply(temp[,1:(ncol(temp)-1)],1,function(x){paste0(x,collapse="-")}))

labels = c(labels,as.character(temp[,i]))

if (i==2) {

print("a")

parents <- c(parents,as.character(temp[,1]))

} else {

parents <- c(parents,apply(temp[,1:(i-1)],1,function(x){paste0(x,collapse="-")}))

}

Freq=c(Freq,temp[,ncol(temp)])

}

}

table4 <- data.frame(ids,labels,parents,Freq)

head(table4)

Affichage

library(plotly)


plot_ly(data = table4, branchvalues = "total",

  type = "treemap", labels = ~labels,

  parents = ~parents, values = ~Freq, ids = ~ids)


5-Les kernels density 2D lorsqu'il y a trop de points pour faire nuages de points classiques

Lorsqu'il y a trop de points à affiche, l'on va comptabiliser la densité des points.

Ainsi on passera de l'exemple ci-dessous au graphique à droite : idéal pour voir qu'il y en réalité 3 types de points !

Suivre ce lien pour construire son propre kernel density 2D (pages sur les autres graphiques y = f(x).

# Reproduire cet exemple

library(MASS) # va générer le kernel (penser à l'installer) (kernel density 2D)

x <- c(rnorm(600,10,3),rnorm(1000,16,4),rnorm(400,20,3))

y <- c(rnorm(600,16,3),rnorm(1000,8,4),rnorm(400,20,3))

plot(x,y,pch=3)

my_kernel <- kde2d(x, y,n=300)

image(my_kernel)

library(raster)

my_raster <- raster(my_kernel)

plot(my_raster)

# n permet de lisser l'image. n grand, image lisse - n petit, image moche