excercice plot en 3D en rotation avec regression et stockage dans un fichier GIF

Postez ici vos questions, réponses, commentaires ou suggestions - Les sujets seront ultérieurement répartis dans les archives par les modérateurs

Modérateur : Groupe des modérateurs

Eric Wajnberg
Messages : 776
Enregistré le : 11 Aoû 2008, 15:37
Contact :

excercice plot en 3D en rotation avec regression et stockage dans un fichier GIF

Messagepar Eric Wajnberg » 06 Déc 2021, 08:42

Bonjour à tous,

Ce post n'est pas vraiment une question que je pose ici, mais le fruit d'élucubrations personnelles sur un exercice que je me suis récemment imposé pour pratiquer la création de plots en 3D (un peu comme un musicien qui s’impose des gammes pour faire le tour d'un sujet).

L'objectif est de construite un scatter plot en 3D, d'y ajuster une surface de régression, de mettre ce graphe en rotation et d’insérer le résultat dans un fichier GIF (par exemple pour le mettre dans une présentation PowerPoint - chose que je fais parfois). En cherchant pas mal sur le web, et dans les docs en tout genre, je n'ai trouvé que trois solutions, qui présentent des avantages et des inconvénients. Je n'ai rien trouvé de parfait. Je vous donne ici ces trois solutions, en pesant le pour et le contre. Si vous avez d'autres idées, elles sont les bienvenues..

Solution 1

Passer par persp, puis mettre le graphe en rotation et sauver le tout dans un fichier GIF avec le package animation. Un code possible est le suivant :

Code : Tout sélectionner

function()
{
        # excercice de rotation d'un graphe 3D avec regression et GIF
        # Option avec persp() et package animation
        #
        # Eric Wajnberg - Décembre 2021
       
        # Charger d'abord library(animation)
        #
        # ensuite il faut appeler cette fonction avec
        # saveGIF(nom_de_cette_fontion(),movie.name="test.gif")
        #
        # Ca produit une image GIF assez pixelisée mais sans fond
        # ce qui peut être pratique pour inserer le GIF dans une présentation PowerPoint
        # (par(bg=..) est pris en compte dans l'affichage à l'écran mais pas dans
        # le fichier GIF généré)
        # ani.options(ani.res=...) n'a pas d'effet
        # Dans ce cas imagick (notamment ffmpeg.exe) a été posé dans la directory donnée ci-dessous

        ani.record(reset = TRUE)
        ani.options(interval = .1)
        ani.options(ffmpeg = 'C:/Program Files/ImageMagick-7.1.0-Q16-HDRI/ffmpeg.exe')

        # construction d'un jeu de données bidon
        x=seq(-3,3,0.5)
        y=x
        my.data=expand.grid(x,y)
        names(my.data)=c("abscisse","ordonnee")
        my.data=as.data.frame(cbind(my.data$abscisse, my.data$ordonnee,
         my.data$abscisse+my.data$ordonnee*my.data$ordonnee+rnorm(length(x)*length(y))))
   names(my.data)=c("abscisse","ordonnee","cote")

        # calcul des données attendu par regression locale et
        # redistribution sous forme matricielle (format imposé par persp)
        attendu=predict(loess(my.data$cote~my.data$abscisse+my.data$ordonnee))
        z=matrix(0,length(x),length(x))
        for (i in 1:(length(x)*length(y)))
        {       
                z[which(x==my.data$abscisse[i]),which(y==my.data$ordonnee[i])]=attendu[i]
        }

        # couleur du fond et taille du texte
        par(bg=rgb(.001,.01,.7,.01),cex=1.5)
        # tracer du plot sous différents angles
        for (i in seq(-180,180,5))
        {
                my.persp=persp(x,y,z, col="lightblue", ticktype="detailed", theta=i, phi=20,
                        cex.lab=1.5, family="serif", lwd=3, zlim=c(min(my.data$cote),max(my.data$cote)))
                # ajout de points
                my.points=trans3d(x=my.data$abscisse,y=my.data$ordonnee,z=my.data$cote,pmat=my.persp)
                points(my.points,pch=16,col="red")
                ani.record()
        }
}


Pour cette solution 1, selon moi :

Avantages :
- Le fond est transparent (pratique pour insérer par exemple dans PowerPoint).

Désavantages :
- Nécessite de créer « à la main » la surface de régression.
- Suppose l’apprentissage du package « animation ».
- Devoir de jongler avec différentes représentations des données (vectoriels et matricielles)
- Le fichier résultat est assez pixélisé sans moyen de régler la résolution.
- L’image ne tourne pas autour de son véritable centre. Elle se « dandine ».
- Nécessité de contrôler « à la main » la rotation (ce qui est également un avantage)

Solution 2

Passer par scatter3d du package rgl, et utiliser les outils de rotation et de sauvegarde en GIF de ce package. Un code possible est le suivant :

Code : Tout sélectionner

function()
{
        # Excercice de rotation d'un graphe 3D avec regression et sortie GIF
        #
        # Option avec scatter3d du package car (et rgl)
        #
        # Eric Wajnberg - Décembre 2021
       
        # Charger d'abord library(car) et library(rgl)
        #
        # par(bg=..) n'est est pris en compte et le fond reste blanc ou
        # noir (argument "bg.col"), ce qui n'est pas "pratique"
        # par exemple pour inserer le GIF dans une présentation PowerPoint

        # construction d'un jeu de données bidon
        x=seq(-3,3,0.5)
        y=x
   my.data=expand.grid(x,y)
   names(my.data)=c("abscisse","ordonnee")
   my.data=as.data.frame(cbind(my.data$abscisse,my.data$ordonnee,
                                                        my.data$abscisse+my.data$ordonnee*my.data$ordonnee+rnorm(length(x)*length(y))))
        names(my.data)=c("abscisse","ordonnee","cote")
       
        # parametre font sur les axes
        par3d(family="serif",cex=1.5, font=2)

        # tracer du plot
        scatter3d(x=my.data$abscisse,z=my.data$ordonnee,y=my.data$cote,fit="smooth",revolutions=0,
                        xlab="x",zlab="y",ylab="z", axis.ticks=TRUE,residuals=FALSE, speed=2.5, axis.col=rep("black",3))

        # sauvegarde l'animation dans un fichier gif (un package supplementaire doit être chargé
        # (par exemple magick)
        movie3d(spin3d(axis = c(0, 1, 0), rpm = 15),duration=4,movie="test",dir = getwd(),convert=TRUE)
}


Pour cette solution 2, selon moi :

Avantages :
- Image « plus élégante ».
- Tourne bien sur elle-même sans se « dandiner »

Désavantages :
- Le fond est blanc ou noir (pas pratique pour insérer par exemple dans PowerPoint).
- Pas de « tick marks » sur les axes.
- Pas de contrôle de la surface de régression (pas paramétrable) (ou alors on peut s'en sortir en combinant persp3d avec plot3d)
- Pas moyen de contrôler le sens de la rotation.

Solution 3

Passer par scatter3D du package plot3D, et s'appuyer ensuite sur les outils du package rgl (via le package plot3Drgl). Un code possible est le suivant :

Code : Tout sélectionner

function()
{
        # Excercice de rotation d'un graphe 3D avec regression et sortie GIF
        #
        # Option avec scatter3D du package plot3D (et rgl), et egalement plot3Drgl
        #
        # Eric Wajnberg - Décembre 2021
       
        # Charger d'abord library(plot3D), library(rgl)
        #
        # par(bg=..) n'est est pris en compte et le fond reste blanc, ce qui n'est pas "pratique"
        # par exemple pour inserer le GIF dans une présentation PowerPoint

        # construction d'un jeu de données bidon
        x=seq(-3,3,0.5)
        y=x
        my.data=expand.grid(x,y)
        names(my.data)=c("abscisse","ordonnee")
        my.data=as.data.frame(cbind(my.data$abscisse,my.data$ordonnee,
                                                        my.data$abscisse+my.data$ordonnee*my.data$ordonnee+rnorm(length(x)*length(y))))
        names(my.data)=c("abscisse","ordonnee","cote")
       
        # calcul des données attendu par regression local
        # redistribution sous forme matricielle (format imposé)
        attendu=predict(loess(my.data$cote~my.data$abscisse+my.data$ordonnee))
        z=matrix(0,length(x),length(x))
        for (i in 1:(length(x)*length(y)))
        {       
                z[which(x==my.data$abscisse[i]),which(y==my.data$ordonnee[i])]=attendu[i]
        }

        # couleur du fond et taille du texte
        par(bg=rgb(.001,.01,.7,.01),cex=1.5)

        # tracer du plot
        scatter3D(x=my.data$abscisse,y=my.data$ordonnee,z=my.data$cote,xlab="x",ylab="y",zlab="z",
                colvar=NULL,col="red",bty="u",surf=list(x=x,y=y,z=z,col="blue",facets=NA),
                col.panel=rgb(.001,.01,.7,.01), pch=16,lwd=3, cex.lab=1.5, family="serif",ticktype="detailed")

        # on bascule ensuite le graphs dans rgl
        # grace à la library plot3Drgl (necessite donc rgl installé)
        library("plot3Drgl")
        plotrgl()

        # sauvegarde l'animation dans un fichier gif (un package supplementaire doit être chargé
        # (par exemple magick)
        movie3d(spin3d(axis = c(0, 0, 1), rpm = 15),duration=4,movie="test",dir = getwd(),convert=TRUE)
}


Pour cette solution 3, selon moi :

Avantages :
- Tourne bien sur elle-même sans se « dandiner »

Désavantages :
- Le fond est blanc (pas pratique pour insérer par exemple dans PowerPoint).
- Nécessite de créer « à la main » la surface de régression.
- Devoir de jongler avec différentes représentations des données (vectoriels et matricielles)
- Pas moyen de contrôler le sens de la rotation.
- Pas moyen de modifier les polices de caractère utilisées sur les axes

Cette solution 3 est en fait un compromis entre les deux premières solutions.

Vous le voyez donc, rien d'idéal ici, et je crois avoir fait le tour de toutes les solutions qu'offrent R. A moins que j'en ai encore loupées ? Toute réaction sur ce post, ou d'autres solutions encore sont les bienvenues.

En espérant également que ces explications seront utiles pour d'autres.

Amicalement, Eric.

Mickael Canouil
Messages : 1315
Enregistré le : 04 Avr 2011, 08:53
Contact :

Re: excercice plot en 3D en rotation avec regression et stockage dans un fichier GIF

Messagepar Mickael Canouil » 06 Déc 2021, 14:55

Bonjour,

as-tu regardé les extensions {rayrender} et {rayshader} ?
https://www.rayshader.com/
http://www.rayrender.net/

Cordialement,
Mickaël
mickael.canouil.fr | rlille.fr

Eric Wajnberg
Messages : 776
Enregistré le : 11 Aoû 2008, 15:37
Contact :

Re: excercice plot en 3D en rotation avec regression et stockage dans un fichier GIF

Messagepar Eric Wajnberg » 06 Déc 2021, 15:03

Waouh, très beaux packages que je ne connaissais pas. Intéressant ! Cependant, ces packages ont un objectif différent, je pense, de la simple problématique que je lance ici. Il s'agit là vraiment de la construction de scènes détaillées avec ombrages, etc, Je vais creuser quand même pour découvrir ceci plus en détail. Merci pour ces indications.

Eric.

Mickael Canouil a écrit :Bonjour,

as-tu regarder les extensions {rayrender} et {rayshader} ?
https://www.rayshader.com/
http://www.rayrender.net/

Cordialement,

Logez Maxime
Messages : 3138
Enregistré le : 26 Sep 2006, 11:35

Re: excercice plot en 3D en rotation avec regression et stockage dans un fichier GIF

Messagepar Logez Maxime » 07 Déc 2021, 15:10

Bonjour,

En complément du gif, est-ce que tu ne peux pas sauver ton objet rgl en html (https://cran.r-project.org/web/packages ... WebGL.html) et l'intégrer à ta diapositive ?
Après je suis étonner que tu dises qu'il n'y a pas de tick-marks sur les axes. Tu peux très bien en faire afficher normalement.
L'avantage si tu gardes cet objet, c'est que justement tu peux le faire bouger à la main, mais ce n'est peut-être pas ce que tu cherches.

Cordialement,
Maxime

Eric Wajnberg
Messages : 776
Enregistré le : 11 Aoû 2008, 15:37
Contact :

Re: excercice plot en 3D en rotation avec regression et stockage dans un fichier GIF

Messagepar Eric Wajnberg » 08 Déc 2021, 09:06

Bonjour Maxime et merci pour ta réponse.
Logez Maxime a écrit :Bonjour,

En complément du gif, est-ce que tu ne peux pas sauver ton objet rgl en html (https://cran.r-project.org/web/packages ... WebGL.html) et l'intégrer à ta diapositive ?

Oui, et il existe également d'autres formats de sortie possible. Mais j'ai vraiment besoin de fichiers gif, notamment pour les mettre dans des présentations powerpoint.
Logez Maxime a écrit :Après je suis étonner que tu dises qu'il n'y a pas de tick-marks sur les axes. Tu peux très bien en faire afficher normalement.

Je ne crois pas. En fait, le seul argument de scratter3d() qui existe pour ce faire est axis.ticks=TRUE, mais ceci affiche les tick-labels, pas les tick-marks. Ou alors j'ai loupé quelque chose.

Eric.

Logez Maxime
Messages : 3138
Enregistré le : 26 Sep 2006, 11:35

Re: excercice plot en 3D en rotation avec regression et stockage dans un fichier GIF

Messagepar Logez Maxime » 09 Déc 2021, 15:40

Bonjour,

Tu peux bien faire afficher des tickmarks, il suffit de voir les exemples qui sont dans le lien posté précédemment.
Par contre la fonction scatter3d n'est pas incluse dans le package rgl, elle s'appuie dessus si je ne dis pas de bêtises (car).
Du coup tu dois pouvoir modifier le code de cette fonction pour intégrer l'option tickmarks.

Cordialement,
Maxime

Eric Wajnberg
Messages : 776
Enregistré le : 11 Aoû 2008, 15:37
Contact :

Re: excercice plot en 3D en rotation avec regression et stockage dans un fichier GIF

Messagepar Eric Wajnberg » 10 Déc 2021, 08:45

Logez Maxime a écrit :Bonjour,

Tu peux bien faire afficher des tickmarks, il suffit de voir les exemples qui sont dans le lien posté précédemment.

Oui, ca veut dire combiner persp3d avec plot3d, comme je l'avais indiqué dans mon post initial.

Logez Maxime a écrit :Par contre la fonction scatter3d n'est pas incluse dans le package rgl, elle s'appuie dessus si je ne dis pas de bêtises (car).
Du coup tu dois pouvoir modifier le code de cette fonction pour intégrer l'option tickmarks.

Oui, elle fait partie effectivement du package car, comme je le disais dans mon post initial. Je n'ai aucune idée, en revanche, de comment modifier le code de cette fonction. Il me semble qu'elle est compilée, et qu'on n'a pas accès à son code.

Dans tous les cas, merci pour ces commentaires,

Eric.

Logez Maxime
Messages : 3138
Enregistré le : 26 Sep 2006, 11:35

Re: excercice plot en 3D en rotation avec regression et stockage dans un fichier GIF

Messagepar Logez Maxime » 13 Déc 2021, 13:10

Bonjour,
Oui, elle fait partie effectivement du package car, comme je le disais dans mon post initial. Je n'ai aucune idée, en revanche, de comment modifier le code de cette fonction. Il me semble qu'elle est compilée, et qu'on n'a pas accès à son code.

Code : Tout sélectionner

scatter3d
function (x, ...)
{
    if (!requireNamespace("rgl"))
        stop("rgl package missing")
    UseMethod("scatter3d")
}
<bytecode: 0x000000001cd6d200>
<environment: namespace:car>

methods("scatter3d")
[1] scatter3d.default* scatter3d.formula*

car:::scatter3d.default
function (x, y, z, xlab = deparse(substitute(x)), ylab = deparse(substitute(y)),
    zlab = deparse(substitute(z)), axis.scales = TRUE, axis.ticks = FALSE,
    revolutions = 0, bg.col = c("white", "black"),
    axis.col = if (bg.col == "white") c("darkmagenta",
        "black", "darkcyan") else c("darkmagenta",
        "white", "darkcyan"), surface.col = carPalette()[-1],
    surface.alpha = 0.5, neg.res.col = "magenta", pos.res.col = "cyan",
    square.col = if (bg.col == "white") "black" else "gray",
    point.col = "yellow", text.col = axis.col, grid.col = if (bg.col ==
        "white") "black" else "gray", fogtype = c("exp2",
        "linear", "exp", "none"), residuals = (length(fit) ==
        1), surface = TRUE, fill = TRUE, grid = TRUE, grid.lines = 26,
    df.smooth = NULL, df.additive = NULL, sphere.size = 1, radius = 1,
    threshold = 0.01, speed = 1, fov = 60, fit = "linear",
    groups = NULL, parallel = TRUE, ellipsoid = FALSE, level = 0.5,
    ellipsoid.alpha = 0.1, id = FALSE, model.summary = FALSE,
    ...)
{
    if (!requireNamespace("rgl"))
        stop("rgl package missing")
 ...
 }
Cordialement,
Maxime



Retourner vers « Questions en cours »

Qui est en ligne

Utilisateurs parcourant ce forum : Google [Bot] et 1 invité