Подтвердить что ты не робот

Создание кругового шлейфа с полым центром (ака по сюжету гоночной трассы)

Меня попросили воссоздать следующий стиль сюжета. (Пожалуйста, проигнорируйте вопрос о том, является ли это хорошим типом визуализации и любезно рассматривает это как добавление красочного элемента в числовую таблицу.)

Большая часть этого довольно проста, но я еще не нашел хорошего способа сделать центр полым. В интересах времени я могу прибегнуть к клоку добавления невидимых фиктивных данных (я опубликую этот подход, если никто другой не сделает этого, но он выглядит менее оптимальным, чем тот, который изменяет тему). Есть ли решение на основе темы или решение без ggplot2 R?

Что мы имитируем

enter image description here

Простой результат ggplot2 (нежелательный заполненный центр)

library(ggplot2)

# make sample dataframe

Category <- c("Electronics", "Appliances", "Books", "Music", "Clothing", 
            "Cars", "Food/Beverages", "Personal Hygiene", 
            "Personal Health/OTC", "Hair Care")
Percent <- c(81, 77, 70, 69, 69, 68, 62, 62, 61, 60)

internetImportance<-data.frame(Category,Percent)

# append number to category name
internetImportance$Category <-
     paste0(internetImportance$Category," - ",internetImportance$Percent,"%")

# set factor so it will plot in descending order 
internetImportance$Category <-
    factor(internetImportance$Category, 
    levels=rev(internetImportance$Category))

# plot

ggplot(internetImportance, aes(x = Category, y = Percent,
    fill = Category)) + 
    geom_bar(width = 0.9, stat="identity") + 
    coord_polar(theta = "y") +
    xlab("") + ylab("") +
    ylim(c(0,100)) +
    ggtitle("Top Product Categories Influenced by Internet") +
    geom_text(data = internetImportance, hjust = 1, size = 3,
              aes(x = Category, y = 0, label = Category)) +
    theme_minimal() +
    theme(legend.position = "none",
          panel.grid.major = element_blank(),
          panel.grid.minor = element_blank(),
          axis.line = element_blank(),
          axis.text.y = element_blank(),
          axis.text.x = element_blank(),
          axis.ticks = element_blank())

enter image description here

Как мы можем построить эти данные с полым центром?

4b9b3361

Ответ 1

Здесь выполняется не-ggplot2 (базовое R-графическое) решение с использованием пакета plotrix, который содержит две приятные функции: draw.circle() и draw.arc():

circBarPlot <- function(x, labels, colors=rainbow(length(x)), cex.lab=1) {
  require(plotrix)
  plot(0,xlim=c(-1.1,1.1),ylim=c(-1.1,1.1),type="n",axes=F, xlab=NA, ylab=NA)
  radii <- seq(1, 0.3, length.out=length(x))
  draw.circle(0,0,radii,border="lightgrey")
  angles <- (1/4 - x)*2*pi
  draw.arc(0, 0, radii, angles, pi/2, col=colors, lwd=130/length(x), lend=2, n=100)
  ymult <- (par("usr")[4]-par("usr")[3])/(par("usr")[2]-par("usr")[1])*par("pin")[1]/par("pin")[2]
  text(x=-0.02, y=radii*ymult, labels=paste(labels," - ", x*100, "%", sep=""), pos=2, cex=cex.lab)
}

circBarPlot(Percent/100, Category)
text(0,0,"GLOBAL",cex=1.5,col="grey")

Это дает мне:

Circular bar plot

Ответ 2

Я думаю, что немедленное исправление заключается в создании некоторых "пустых" записей. Я бы создал internetImportance data.frame следующим образом:

Category <- c("Electronics", "Appliances", "Books", "Music", "Clothing", 
        "Cars", "Food/Beverages", "Personal Hygiene", 
        "Personal Health/OTC", "Hair Care")
Percent <- c(81, 77, 70, 69, 69, 68, 62, 62, 61, 60)

internetImportance <- data.frame(Category,Percent)

len <- 4
df2 <- data.frame(Category = letters[1:len], Percent = rep(0, len), 
                                 Category2 = rep("", len))
internetImportance$Category2 <- 
 paste0(internetImportance$Category," - ",internetImportance$Percent,"%")

# append number to category name
internetImportance <- rbind(internetImportance, df2)

# set factor so it will plot in descending order 
internetImportance$Category <-
    factor(internetImportance$Category, 
    levels=rev(internetImportance$Category))

И затем я нарисую ggplot2 с помощью fill=category2 следующим образом:

ggplot(internetImportance, aes(x = Category, y = Percent,
    fill = Category2)) + 
    geom_bar(width = 0.9, stat="identity") + 
    coord_polar(theta = "y") +
    xlab("") + ylab("") +
    ylim(c(0,100)) +
    ggtitle("Top Product Categories Influenced by Internet") +
    geom_text(data = internetImportance, hjust = 1, size = 3,
              aes(x = Category, y = 0, label = Category2)) +
    theme_minimal() +
    theme(legend.position = "none",
          panel.grid.major = element_blank(),
          panel.grid.minor = element_blank(),
          axis.line = element_blank(),
          axis.text.y = element_blank(),
          axis.text.x = element_blank(),
          axis.ticks = element_blank())

Это дает мне:

enter image description here

Вы можете добавить geom_text(label="GLOBAL", x=.5, y=.5, size=4) + до theme_minimal, чтобы добавить текст GLOBAL.

Ответ 3

Другое решение base, которое не полагается на пакет plotrix:

circular.barplot<-function(values, labels, col, cex){
    df<-data.frame(values=sort(values), labels=labels[order(values)])
    col<-col[order(values)]
    plot(NA,xlim=c(-1.3,1.3),ylim=c(-1.3,1.3),axes=F, xlab=NA, ylab=NA, asp=1)
    t<-sapply(df$values,function(x).5*pi-seq(0, 2*pi*x/100,length=1000))
    x<-sapply(1:nrow(df),function(x)(.3+x/nrow(df))*cos(t[,x]))
    y<-sapply(1:nrow(df),function(x)(.3+x/nrow(df))*sin(t[,x]))
    for(i in 1:nrow(df)){
        lines(x=x[,i],y=y[,i],col=col[i],lwd=10,lend=1)
        text(x[1,i],y[1,i],paste(df$labels[i]," - ",df$values[i],"%",sep=""),
             pos=2,cex=cex)
        }
    }

enter image description here

Ответ 4

Поскольку график является круговым, его можно легко сделать с помощью пакета circlize.

Сначала данные:

Category <- c("Electronics", "Appliances", "Books", "Music", "Clothing", 
        "Cars", "Food/Beverages", "Personal Hygiene", 
        "Personal Health/OTC", "Hair Care")
Percent <- c(81, 77, 70, 69, 69, 68, 62, 62, 61, 60)
color = rainbow(length(Percent))

Переверните три вектора, так как circlize по умолчанию добавляет каждый элемент изнутри внутрь:

Category = rev(Category)
Percent = rev(Percent)
color = rev(color)

Если вы изображаете круг, это изогнутая область сюжета, то это просто добавление прямоугольников, линий и текстов.

library(circlize)

par(mar = c(1, 1, 1, 1))
circos.par("start.degree" = 90)
circos.initialize("a", xlim = c(0, 100)) # 'a` just means there is one sector
circos.trackPlotRegion(ylim = c(0.5, length(Percent)+0.5), track.height = 0.8, 
    bg.border = NA, panel.fun = function(x, y) {
    xlim = get.cell.meta.data("xlim") # in fact, it is c(0, 100)
    for(i in seq_along(Percent)) {
        circos.lines(xlim, c(i, i), col = "#CCCCCC")
        circos.rect(0, i - 0.45, Percent[i], i + 0.45, col = color[i], 
            border = "white")
        circos.text(xlim[2], i, paste0(Category[i], " - ", Percent[i], "%"), 
            adj = c(1, 0.5)) 
    }
})
circos.clear()

text(0, 0, "GLOBAL", col = "#CCCCCC")

enter image description here