Czy istnieje nazwa tego wykresu - coś w rodzaju skrzyżowania wykresu kołowego i wykresu mekko


9

Czy poniżej znajduje się nazwa tego rodzaju wykresu (pochodzącego z nowozelandzkiego Ministerstwa Biznesu, Innowacji i Zatrudnienia , dla którego pracuję, ale nie byłem zaangażowany w tworzenie tej fabuły)? Składa się z prostokątów, w których obszar jest proporcjonalny do zmiennej, i przypomina rodzaj skrzyżowania wykresu kołowego, wykresu mozaiki i wykresu mekko. Być może jest to najbliżej fabuły mekko, ale ma tę komplikację, że nie pracujemy z kolumnami, ale bardziej złożoną układanką.

Oryginał wygląda trochę lepiej, ponieważ między prostokątami dla każdego regionu znajdują się białe granice.

Zaskakujące, wydaje mi się, że nie jest to wcale taka zła grafika statystyczna, chociaż można to poprawić poprzez lepsze wykorzystanie mapowania kolorów na coś znaczącego. New York Times wykorzystał potężną interaktywną wersję pokazującą budżet USA na 2011 rok .

Ciekawym wyzwaniem jest wymyślenie automatycznego algorytmu do narysowania jednego i nadania mu rozsądnego wyglądu. Prostokąty muszą mieć różne proporcje, w dopuszczalnym zakresie.

wprowadź opis zdjęcia tutaj


Rezultat końcowy projektu, który rozpoczął się od tego pytania, można zobaczyć w interaktywnym narzędziu internetowym, do którego link znajduje się tutaj: mbie.govt.nz/what-we-do/business-growth-agenda/regions
Peter Ellis

Odpowiedzi:



12

Pytanie brzmi jak nazwa, ale jak dobrze działa, można również dyskutować.

Oto coś o wiele bardziej prozaicznego jako alternatywa, poziomy wykres słupkowy.

wprowadź opis zdjęcia tutaj

To, co moglibyśmy chcieć zrobić z takim wykresem, różni się od zrozumienia ogólnego wzorca do analizy poszczególnych przypadków (co z Hawke's Bay i tak dalej). Twierdzę, że oba są łatwiejsze dzięki wykresowi słupkowemu. Małe szczegóły to to, że używam małych liter w tytułach i nazwach, gdzie jest to łatwe i nie powtarzam znaku%. Z grubsza naśladowałem kodowanie kolorów, nie wiedząc, co to znaczy, więc jest to tak samo jasne lub niejasne, jak to, co skopiowałeś.

Sugeruję, że część atrakcyjności map tremaps polega na ich względnej nowości. Mogą działać równie dobrze lub lepiej niż wykresy słupkowe, jeśli istnieją dziesiątki nazw, które można rozłożyć na dwuwymiarowym obszarze, a nie wymienić w długiej kolumnie. Ale w przypadku 15 nazwisk wykres słupkowy pozostaje moim zdaniem silnym konkurentem.

Cieszę się z każdego, kto woli tutaj wykres punktowy (Cleveland). Pionowy wykres słupkowy napotkałby trudności w wygodnym umieszczeniu nazw regionów. (Wystarczy wyobrazić sobie obracanie tego wykresu, aby to zobaczyć.) Podoba mi się również pomysł podania liczb, chociaż konserwatyści nie lubią mieszać pomysłów na wykresy i tabele.

Wykres został narysowany w Stata.


4
Będę musiał go wykopać, ale jeśli moja pamięć służy mi poprawnie, jedną z pierwotnych motywów mapy drzewa była hierarchiczna organizacja informacji (tj. Aby umożliwić ci przeglądanie łącznego rozmiaru różnych poziomów hierarchii) i dla wielu innych liczb. Zamiarem nigdy nie były małe listy liczb i miały one bardziej eksploracyjny urok, patrz Wytyczne percepcyjne dotyczące tworzenia prostokątnych szablonów ( Kong i in. 2010 )
Andy W

1
Takie też mam wrażenie, stąd nazwa mapy. Widoczny jest tylko jeden poziom hierarchii.
Nick Cox,

4
Bill Shneiderman zebrał niezłą historię map z linkami do niektórych odpowiednich publikacji ( cs.umd.edu/hcil/treemap-history ). Mapy trej początkowo miały wyświetlać wielopoziomową hierarchię w mniej zaśmiecony sposób niż dendrogramy lub drzewa, i początkowo były używane do wizualizacji zawartości dysków twardych. Obecnie mapy drzew są wykorzystywane do wizualizacji dużych drzew filogenetycznych (pokazują związki między gatunkami), między innymi. Więcej przykładów można znaleźć w artykule Shneidermana na stronie perceptualedge.com/articles/b-eye/treemaps.pdf .
JTT

Dzięki; za co warto się zgodzić w tym konkretnym przypadku.
Peter Ellis,

3

Edytuj / dodaj

Od tego czasu odkryłem, że pakiet treemap daje znacznie lepszy wynik niż funkcja map.market () wspomniana (i dostosowana) poniżej; ale zostawię moją odpowiedź ze względów historycznych.

Oryginalna odpowiedź

Dziękuję za odpowiedzi. Opierając się na przepływającym łączu danych dostarczonym przez @JTT, ale nie lubiąc ręcznie poprawiać w programie Illustrator lub Inkscape, aby uzyskać rozsądną grafikę, poprawiłem funkcję map.market () w pakiecie portfolio Jeffa Enosa i Davida Kane'a, aby była bardziej kontrolowane przez użytkownika, etykiety różnią się w zależności od rozmiaru prostokąta i unikają kontrastów czerwono-zielonych. Przykładowe użycie:

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

wprowadź opis zdjęcia tutaj

Co do wartości, zgadzam się również z @NickCox, że w przykładzie w moim pierwotnym pytaniu wykres kropkowy jest lepszy. Następuje kod mojej dostosowanej funkcji treemap ().

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

Ten kod bez wątpienia przyda się. Nie chcę przeciągać dyskusji na obszary, do których nie pójdzie, ale czy przykład jest dość arbitralny, czy też istnieje uzasadnienie pozwalające, aby obszary reprezentowały ceny akcji? Czego powinniśmy zobaczyć lub szukać na tej fabule? (Nie jestem wrogi, po prostu zupełnie niedoświadczony w próbach użycia tego projektu, chociaż widziałem wiele przykładów.)
Nick Cox

1
Właśnie wziąłem przykład z pliku pomocy dla map.market () autorstwa Enosa i Kane'a. Zastanawiając się nad tym, nie rozumiem, dlaczego zdecydowali się na cenę pokazaną w obszarze; bardziej rozsądnym środkiem byłoby z pewnością wykazanie całkowitej kapitalizacji, tj. ceny x liczby akcji (albo liczby akcji na rynku, albo tylko liczby akcji, które posiadam w zależności od celu). Wtedy dobrze byłoby intuicyjnie wykorzystać fabułę, aby pokazać znaczenie różnych zapasów.
Peter Ellis

Zaskoczyło mnie również użycie ceny.
Nick Cox,

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.