Existe um nome para este gráfico - uma espécie de cruzamento entre um gráfico de pizza e um gráfico de mekko

9

Existe um nome para este tipo de gráfico abaixo (proveniente do Ministério de Negócios, Inovação e Emprego da Nova Zelândia , para quem trabalho, mas não estava envolvido na criação dessa trama)? Consiste em retângulos em que a área é proporcional a uma variável e se assemelha a uma espécie de cruzamento entre um gráfico de pizza, um gráfico de mosaico e um gráfico de mekko. Talvez seja o mais próximo de um gráfico de mekko, mas tem a complicação de não estarmos trabalhando com colunas, mas com um quebra-cabeça mais complexo.

O original parece um pouco melhor, pois há bordas brancas entre os retângulos para cada região.

Surpreendentemente, ele realmente me parece um gráfico estatístico não muito ruim, embora possa ser melhorado com o melhor uso de cores mapeadas para algo significativo. Uma versão interativa poderosa que mostra o orçamento dos EUA para 2011 foi usada pelo New York Times .

Um desafio interessante é pensar em um algoritmo automático para desenhar um e torná-lo razoável também. É necessário permitir que os retângulos tenham proporções diferentes, dentro de um intervalo aceitável.

insira a descrição da imagem aqui

Peter Ellis
fonte
O resultado final do projeto que começou com esta pergunta pode ser visto na ferramenta da Web interativo ligado a partir daqui: mbie.govt.nz/what-we-do/business-growth-agenda/regions
Peter Ellis

Respostas:

12

A questão é o nome, mas o quão bem ele funciona também está aberto à discussão.

Aqui está algo muito mais prosaico como alternativa, um gráfico de barras horizontal.

insira a descrição da imagem aqui

O que poderíamos querer fazer com esse gráfico varia de alguma compreensão do padrão geral a uma análise minuciosa de casos individuais (e quanto a Hawke's Bay, e assim por diante). Eu diria que ambos são mais fáceis com um gráfico de barras. Pequenos detalhes são que eu uso letras minúsculas em títulos e nomes onde é fácil e não repito o sinal de%. Eu praticamente imitei o código de cores sem descobrir o que significa, para que fique tão claro ou obscuro quanto o que você copiou.

Sugiro que parte do apelo dos mapas de árvores esteja em sua relativa novidade. Eles podem funcionar tão bem quanto ou melhor que os gráficos de barras se houver dezenas de nomes, que podem ser espalhados por uma área bidimensional em vez de listados em uma coluna longa. Mas para 15 ou mais nomes, um gráfico de barras continua sendo um forte concorrente na minha opinião.

Estou feliz com quem prefere um gráfico de pontos (Cleveland) aqui. Um gráfico de barras vertical enfrentaria a dificuldade de colocar os nomes da região confortavelmente. (Imagine girar esse gráfico para ver isso.) Eu também gosto da ideia de fornecer os números, embora os conservadores não gostem de misturar idéias de gráficos e tabelas.

O gráfico foi desenhado em Stata.

Nick Cox
fonte
4
Vou ter que desenterrá-lo, mas se minha memória me servir corretamente, uma das motivações originais do mapa da árvore foi para uma organização hierárquica de informações (ou seja, para permitir que você veja o tamanho combinado de diferentes níveis da hierarquia) e para muitos mais números. A intenção nunca foi para pequenas listas de números e teve um apelo mais exploratório, consulte Diretrizes Perceptivas para a Criação de Mapas de Árvores Retangulares ( Kong et al. 2010 )
Andy W
1
Essa é a minha impressão também, daí o nome treemap. Apenas um nível de hierarquia é óbvio aqui.
Nick Cox
4
Bill Shneiderman reuniu um bom histórico de mapas de árvores com links para algumas publicações relevantes ( cs.umd.edu/hcil/treemap-history ). Os mapas de árvore foram inicialmente projetados para exibir uma hierarquia multinível de maneira menos confusa do que os dendrogramas ou as árvores, e foram usados ​​inicialmente para visualizar o conteúdo de discos rígidos. Atualmente, os mapas de árvores são utilizados para visualizar grandes árvores filogenéticas (mostram relações entre espécies), entre outras aplicações. Para mais exemplos, consulte o artigo de Shneiderman em perceptualedge.com/articles/b-eye/treemaps.pdf .
JTT 05/09
Obrigado; pelo que vale a pena, concordo neste caso em particular.
Peter Ellis
3

Editar / adição

Desde então, descobri que o pacote treemap oferece um resultado muito melhor do que a função map.market () mencionada (e adaptada) abaixo; mas deixarei minha resposta por razões históricas.

Resposta original

Obrigado pelas respostas. Com base no link de dados em fluxo fornecido pelo @JTT, mas não gostando da necessidade de ajustar manualmente no Illustrator ou no Inkscape apenas para obter um gráfico razoável, ajustei a função map.market () no pacote de portfólio de Jeff Enos e David Kane para torná-lo mais controladas pelo usuário, os rótulos variam de acordo com o tamanho do retângulo e evitam contrastes vermelho-verde. Exemplo de uso:

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")
    )

insira a descrição da imagem aqui

Pelo que vale, também concordo com o @NickCox que, no exemplo da minha pergunta original, um gráfico de pontos é superior. Código da minha função treemap () adaptada a seguir.

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)
}
Peter Ellis
fonte
Esse código sem dúvida será útil. Não quero arrastar a discussão para áreas onde ela não irá, mas o exemplo é bastante arbitrário ou existe uma justificativa para permitir que as áreas representem os preços das ações? O que devemos ver ou procurar nesse enredo? (Eu não sou hostil, apenas totalmente inexperiente com a tentativa de usar este projeto para o real, embora eu tenha visto muitos exemplos.)
Nick Cox
1
Na verdade, eu apenas peguei um exemplo disso no arquivo de ajuda para map.market () de Enos e Kane. Refletindo sobre isso, não vejo por que eles escolheram ter o preço de exibição da área; uma medida mais sensata seria certamente mostrar a capitalização total, ou seja, preço x número de ações (número de ações no mercado ou apenas o número de ações que possuo dependendo da finalidade). Então você teria um bom uso intuitivo da plotagem para mostrar a importância das diferentes ações.
91113 Peter Ellis
Também fiquei intrigado com o uso do preço.
Nick Cox
1

É um mapa da árvore, você pode fazer isso facilmente com o Tableau 8 e o Tableau Public gratuito, veja o exemplo aqui: http://www.tableausoftware.com/new-features/new-view-types . Você também pode ver @ neste URL que o Treemap pode ser combinado com o Gráfico de barras

Andrew Pandre
fonte