C'è un nome per questo grafico - una specie di incrocio tra un grafico a torta e un diagramma di mekko


9

C'è un nome per questo tipo di grafico qui sotto (proveniente dal Ministero degli affari, dell'innovazione e dell'occupazione della Nuova Zelanda , per il quale lavoro ma non sono stato coinvolto nella creazione di questo complotto)? È costituito da rettangoli in cui l'area è proporzionale a una variabile e assomiglia a una sorta di croce tra un grafico a torta, una trama a mosaico e una trama mekko. È forse il più vicino a un complotto di mekko ma ha la complicazione che non stiamo lavorando con le colonne ma con un puzzle più complesso.

L'originale ha un aspetto leggermente migliore in quanto vi sono bordi bianchi tra i rettangoli per ogni regione.

Sorprendentemente, in realtà mi sembra un grafico statistico non troppo male, anche se potrebbe essere migliorato attraverso un migliore uso del colore mappato a qualcosa di significativo. Una nuova versione interattiva che mostra il bilancio USA 2011 è stata utilizzata dal New York Times .

Una sfida interessante è pensare a un algoritmo automatico per disegnarne uno e farlo sembrare anche ragionevole. I rettangoli devono avere proporzioni diverse, entro un intervallo accettabile.

inserisci qui la descrizione dell'immagine


Il risultato finale del progetto iniziato con questa domanda può essere visto nello strumento web interattivo collegato da qui: mbie.govt.nz/what-we-do/business-growth-agenda/regions
Peter Ellis

Risposte:



12

La domanda è il nome, ma quanto bene funziona è anche aperto alla discussione.

Ecco qualcosa di molto più prosaico in alternativa, un grafico a barre orizzontale.

inserisci qui la descrizione dell'immagine

Ciò che potremmo voler fare con un tale grafico varia da una comprensione del modello generale a un esame dei singoli casi (che dire di Hawke's Bay, e così via). Affermerei che entrambi sono più facili con un grafico a barre. Piccoli dettagli sono che uso lettere minuscole in titoli e nomi dove è facile e non ripeto il segno%. Ho approssimativamente imitato il codice colore senza scoprire cosa significhi, quindi è altrettanto chiaro o oscuro, come quello che hai copiato.

Suggerisco che parte del fascino delle treemap risieda nella loro relativa novità. Potrebbero funzionare meglio o meglio dei grafici a barre se ci sono dozzine di nomi, che possono essere distribuiti su un'area bidimensionale anziché elencati in una lunga colonna. Ma per circa 15 nomi, un grafico a barre rimane un forte concorrente a mio avviso.

Sono contento di chiunque preferisca un diagramma a punti (Cleveland) qui. Un grafico a barre verticali incontrerebbe la difficoltà di posizionare comodamente i nomi delle regioni. (Immagina di ruotare questo grafico per vederlo.) Mi piace anche l'idea di dare i numeri, anche se ai conservatori non piace mescolare idee di grafici e tabelle.

Il grafico è stato disegnato in Stata.


4
Dovrò scavare, ma se la mia memoria mi serve correttamente una delle motivazioni originali della mappa ad albero era per un'organizzazione gerarchica di informazioni (cioè per consentire di visualizzare le dimensioni combinate di diversi livelli della gerarchia) e per molti più numeri. L'intento non è mai stato per piccoli elenchi di numeri e aveva un fascino più esplorativo, vedi Linee guida percettive per la creazione di mappe rettangolari ( Kong et al. 2010 )
Andy W

1
Anche questa è la mia impressione, da cui il nome treemap. Solo un livello di una gerarchia è ovvio qui.
Nick Cox,

4
Bill Shneiderman ha messo insieme una bella storia di mappe ad albero con collegamenti ad alcune pubblicazioni pertinenti ( cs.umd.edu/hcil/treemap-history ). Inizialmente le treemap avevano lo scopo di mostrare una gerarchia multilivello in un modo meno ingombrante di quanto non potessero essere i dendrogrammi o gli alberi, e inizialmente furono usate per visualizzare il contenuto dei dischi rigidi. Oggi le treemap vengono utilizzate per visualizzare grandi alberi filogenetici (mostrano relazioni tra le specie), tra le altre applicazioni. Per altri esempi, vedere l'articolo di Shneiderman su perceptualedge.com/articles/b-eye/treemaps.pdf .
JTT

Grazie; per quello che vale, sono d'accordo in questo caso particolare.
Peter Ellis,

3

Modifica / aggiunta

Da allora ho scoperto che il pacchetto treemap dà un risultato molto migliore rispetto alla funzione map.market () menzionata (e adattata) di seguito; ma lascerò la mia risposta per motivi storici.

Risposta originale

Grazie per le risposte Basandomi sul collegamento dati fluido fornito da @JTT ma non apprezzando la necessità di modificare manualmente in Illustrator o Inkscape solo per ottenere un grafico ragionevole, ho modificato la funzione map.market () in Jeff Enos e il pacchetto portfolio di David Kane per renderlo più controllate dall'utente, le etichette variano in base alla dimensione del rettangolo ed evitano contrasti rosso-verde. Esempio di utilizzo:

library(portfolio)
library(extrafont)
data(dow.jan.2005)

with(dow.jan.2005, 
    treemap(id    = symbol,
        area  = price,
        group = sector,
        color = 100 * month.ret,
        labsc = .12,  # user-chosen scaling of labels 
        fontfamily="Comic Sans MS")
    )

inserisci qui la descrizione dell'immagine

Per quello che vale, sono anche d'accordo con @NickCox che nell'esempio nella mia domanda originale un diagramma a punti è superiore. Segue il codice della mia funzione treemap () adattata.

treemap <- function (id, area, group, color, scale = NULL, lab = c(group = TRUE, 
    id = FALSE), low="red", middle="grey60", high="blue", main = "Map of the Market", labsc = c(.5, 1), print = TRUE, ...) 
{
    # Adapted by Peter Ellis from map.market() by Jeff Enos and David Kane in the portfolio package on CRAN
    # See map.market for the original helpfile.  The changes are:
    # 1. low, middle and high are user-set color ramp choices
    # 2. The font size now varies with the area of the rectangle being labelled; labsc is a scaling parameter to make it look ok.
    #    First element of labsc is scaling parameter for size of group labels.  Second element is scaling for id labels.
    # 3. ... extra arguments to be passed to gpar() when drawing labels; expected use is for fontfamily="whatever"
    require(portfolio)
    if (any(length(id) != length(area), length(id) != length(group), 
        length(id) != length(color))) {
        stop("id, area, group, and color must be the same length.")
    }
    if (length(lab) == 1) {
        lab[2] <- lab[1]
    }
    if (missing(id)) {
        id <- seq_along(area)
        lab["id"] <- FALSE
    }
    stopifnot(all(!is.na(id)))
    data <- data.frame(label = id, group, area, color)
    data <- data[order(data$area, decreasing = TRUE), ]
    na.idx <- which(is.na(data$area) | is.na(data$group) | is.na(data$color))
    if (length(na.idx)) {
        warning("Stocks with NAs for area, group, or color will not be shown")
        data <- data[-na.idx, ]
    }
    zero.area.idx <- which(data$area == 0)
    if (length(zero.area.idx)) {
        data <- data[-zero.area.idx, ]
    }
    if (nrow(data) == 0) {
        stop("No records to display")
    }
    data$color.orig <- data$color
    if (is.null(scale)) {
        data$color <- data$color * 1/max(abs(data$color))
    }
    else {
        data$color <- sapply(data$color, function(x) {
            if (x/scale > 1) 
                1
            else if (-1 > x/scale) 
                -1
            else x/scale
        })
    }
    data.by.group <- split(data, data$group, drop = TRUE)
    group.data <- lapply(data.by.group, function(x) {
        sum(x[, 3])
    })
    group.data <- data.frame(area = as.numeric(group.data), label = names(group.data))
    group.data <- group.data[order(group.data$area, decreasing = TRUE), 
        ]
    group.data$color <- rep(NULL, nrow(group.data))
    color.ramp.pos <- colorRamp(c(middle, high))
    color.ramp.neg <- colorRamp(c(middle, low))
    color.ramp.rgb <- function(x) {
        col.mat <- mapply(function(x) {
            if (x < 0) {
                color.ramp.neg(abs(x))
            }
            else {
                color.ramp.pos(abs(x))
            }
        }, x)
        mapply(rgb, col.mat[1, ], col.mat[2, ], col.mat[3, ], 
            max = 255)
    }
    add.viewport <- function(z, label, color, x.0, y.0, x.1, 
        y.1) {
        for (i in 1:length(label)) {
            if (is.null(color[i])) {
                filler <- gpar(col = "blue", fill = "transparent", 
                  cex = 1)
            }
            else {
                filler.col <- color.ramp.rgb(color[i])
                filler <- gpar(col = filler.col, fill = filler.col, 
                  cex = 0.6)
            }
            new.viewport <- viewport(x = x.0[i], y = y.0[i], 
                width = (x.1[i] - x.0[i]), height = (y.1[i] - 
                  y.0[i]), default.units = "npc", just = c("left", 
                  "bottom"), name = as.character(label[i]), clip = "on", 
                gp = filler)
            z <- append(z, list(new.viewport))
        }
        z
    }
    squarified.treemap <- function(z, x = 0, y = 0, w = 1, h = 1, 
        func = add.viewport, viewport.list) {
        cz <- cumsum(z$area)/sum(z$area)
        n <- which.min(abs(log(max(w/h, h/w) * sum(z$area) * 
            ((cz^2)/z$area))))
        more <- n < length(z$area)
        a <- c(0, cz[1:n])/cz[n]
        if (h > w) {
            viewport.list <- func(viewport.list, z$label[1:n], 
                z$color[1:n], x + w * a[1:(length(a) - 1)], rep(y, 
                  n), x + w * a[-1], rep(y + h * cz[n], n))
            if (more) {
                viewport.list <- Recall(z[-(1:n), ], x, y + h * 
                  cz[n], w, h * (1 - cz[n]), func, viewport.list)
            }
        }
        else {
            viewport.list <- func(viewport.list, z$label[1:n], 
                z$color[1:n], rep(x, n), y + h * a[1:(length(a) - 
                  1)], rep(x + w * cz[n], n), y + h * a[-1])
            if (more) {
                viewport.list <- Recall(z[-(1:n), ], x + w * 
                  cz[n], y, w * (1 - cz[n]), h, func, viewport.list)
            }
        }
        viewport.list
    }
    map.viewport <- viewport(x = 0.05, y = 0.05, width = 0.9, 
        height = 0.75, default.units = "npc", name = "MAP", just = c("left", 
            "bottom"))
    map.tree <- gTree(vp = map.viewport, name = "MAP", children = gList(rectGrob(gp = gpar(col = "dark grey"), 
        name = "background")))
    group.viewports <- squarified.treemap(z = group.data, viewport.list = list())
    for (i in 1:length(group.viewports)) {
        this.group <- data.by.group[[group.data$label[i]]]
        this.data <- data.frame(this.group$area, this.group$label, 
            this.group$color)
        names(this.data) <- c("area", "label", "color")
        stock.viewports <- squarified.treemap(z = this.data, 
            viewport.list = list())
        group.tree <- gTree(vp = group.viewports[[i]], name = group.data$label[i])
        for (s in 1:length(stock.viewports)) {
            stock.tree <- gTree(vp = stock.viewports[[s]], name = this.data$label[s], 
                children = gList(rectGrob(name = "color")))
            if (lab[2]) {
                stock.tree <- addGrob(stock.tree, textGrob(x = unit(1, 
                  "lines"), y = unit(1, "npc") - unit(1, "lines"), 
                  label = this.data$label[s], gp = gpar(col = "white", fontsize=this.data$area[s] * labsc[2], ...), 
                  name = "label", just = c("left", "top")))
            }
            group.tree <- addGrob(group.tree, stock.tree)
        }
        group.tree <- addGrob(group.tree, rectGrob(gp = gpar(col = "grey"), 
            name = "border"))
        if (lab[1]) {
            group.tree <- addGrob(group.tree, textGrob(label = group.data$label[i], 
                name = "label", gp = gpar(col = "white", fontsize=group.data$area[i] * labsc[1], ...)))
        }
        map.tree <- addGrob(map.tree, group.tree)
    }
    op <- options(digits = 1)
    top.viewport <- viewport(x = 0.05, y = 1, width = 0.9, height = 0.2, 
        default.units = "npc", name = "TOP", , just = c("left", 
            "top"))
    legend.ncols <- 51
    l.x <- (0:(legend.ncols - 1))/(legend.ncols)
    l.y <- unit(0.25, "npc")
    l.cols <- color.ramp.rgb(seq(-1, 1, by = 2/(legend.ncols - 
        1)))
    if (is.null(scale)) {
        l.end <- max(abs(data$color.orig))
    }
    else {
        l.end <- scale
    }
    top.list <- gList(textGrob(label = main, y = unit(0.7, "npc"), 
        just = c("center", "center"), gp = gpar(cex = 2, ...)), segmentsGrob(x0 = seq(0, 
        1, by = 0.25), y0 = unit(0.25, "npc"), x1 = seq(0, 1, 
        by = 0.25), y1 = unit(0.2, "npc")), rectGrob(x = l.x, 
        y = l.y, width = 1/legend.ncols, height = unit(1, "lines"), 
        just = c("left", "bottom"), gp = gpar(col = NA, fill = l.cols), 
        default.units = "npc"), textGrob(label = format(l.end * 
        seq(-1, 1, by = 0.5), trim = TRUE), x = seq(0, 1, by = 0.25), 
        y = 0.1, default.units = "npc", just = c("center", "center"), 
        gp = gpar(col = "black", cex = 0.8, fontface = "bold")))
    options(op)
    top.tree <- gTree(vp = top.viewport, name = "TOP", children = top.list)
    mapmarket <- gTree(name = "MAPMARKET", children = gList(rectGrob(gp = gpar(col = "dark grey", 
        fill = "dark grey"), name = "background"), top.tree, 
        map.tree))
    if (print) {
        grid.newpage()
        grid.draw(mapmarket)
    }
    invisible(mapmarket)
}

Quel codice sarà senza dubbio utile. Non voglio trascinare la discussione in aree dove non andrà, ma l'esempio è abbastanza arbitrario o esiste una logica per lasciare che le aree rappresentino i prezzi delle azioni? Cosa dovremmo vedere o cercare in questa trama? (Non sono ostile, solo totalmente inesperto nel provare a usare questo design per davvero, anche se ho visto molti esempi.)
Nick Cox,

1
In realtà ho appena preso esempio dal file di aiuto per map.market () di Enos e Kane. Riflettendoci non vedo perché abbiano scelto di mostrare il prezzo dell'area; una misura più sensata sarebbe sicuramente quella di mostrare la capitalizzazione totale, cioè il prezzo x il numero di azioni (o il numero di azioni sul mercato o solo il numero di azioni che possiedo a seconda dello scopo). Quindi avresti un buon uso intuitivo della trama per mostrare l'importanza delle diverse scorte.
Peter Ellis,

Ero anche perplesso dall'uso del prezzo.
Nick Cox,

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.