wawawa
wawawa

Reputation: 3345

How can I avoid pie chart&legend overlap in R?

I wanna create a pie chart of crime types,and add a legend on the right hand,but I tried many times to avoid overlapping,doesn't work at all.

table(dd$Primary.Type.new)

                 ARSON                    ASSAULT                    BATTERY                   BURGLARY 
                   833                      30743                      91237                      29298 
       CRIMINAL DAMAGE          CRIMINAL TRESPASS         DECEPTIVE PRACTICE                   HOMICIDE 
                 57539                      14353                      17472                        640 
            KIDNAPPING        MOTOR VEHICLE THEFT                  NARCOTOCS OFFENSE INVOLVING CHILDREN 
                   517                      23724                      55685                       3347 
         OTHER OFFENSE             PUBLIC OFFENSE     PUBLIC PEACE VIOLATION                    ROBBERY 
                 30878                       3833                       3632                      18891 
             SEX_CRIME                      THEFT          WEAPONS VIOLATION 
                  9331                     103255                       4792

Type <- table(dd$Primary.Type.new)

Here's that from dput():

structure(c(ARSON = 833L, ASSAULT = 30743L, BATTERY = 91237L, 
BURGLARY = 29298L, `CRIMINAL DAMAGE` = 57539L, `CRIMINAL TRESPASS` = 14353L, 
`DECEPTIVE PRACTICE` = 17472L, HOMICIDE = 640L, KIDNAPPING = 517L, 
`MOTOR VEHICLE THEFT` = 23724L, NARCOTOCS = 55685L, `OFFENSE INVOLVING CHILDREN` = 3347L, 
`OTHER OFFENSE` = 30878L, `PUBLIC OFFENSE` = 3833L, `PUBLIC PEACE VIOLATION` = 3632L, 
ROBBERY = 18891L, `SEX CRIME` = 9331L, THEFT = 103255L, `WEAPONS VIOLATION` = 4792L
), .Dim = 19L, .Dimnames = list(. = c("ARSON", "ASSAULT", "BATTERY", 
"BURGLARY", "CRIMINAL DAMAGE", "CRIMINAL TRESPASS", "DECEPTIVE PRACTICE", 
"HOMICIDE", "KIDNAPPING", "MOTOR VEHICLE THEFT", "NARCOTOCS", 
"OFFENSE INVOLVING CHILDREN", "OTHER OFFENSE", "PUBLIC OFFENSE", 
"PUBLIC PEACE VIOLATION", "ROBBERY", "SEX CRIME", "THEFT", "WEAPONS VIOLATION"
)), class = "table") -> Type

piepercent<- round(100*Type/sum(Type), 1)

pie(Type, edges = 200, radius = 0.8,
clockwise = FALSE,angle = 45, col = rainbow(length(Type)), main = "Pie Chart of Primary Crime Types", labels = piepercent,labelcex = 0.8)

legend("right", inset = .05, title = "Primary Crime Type",legend= dd$Primary.Type.new,fill = rainbow(length(Type)), horiz=FALSE,cex = 0.6)

enter image description here

I tried to use par(), but doestn't work.

and BTW how can I change the labels into percentage? such as convert 20.7 into 20.7%. Thank you very much.

Update I also tried 3D piechart

library(plotrix)
pie3D(Type,labels = piepercent,explode = 0.1, main = "3D Pie Chart of 
Primary Crime Types", labelcex = 0.8)
legend("bottom", inset = .05, title = "Primary Crime Type",legend= dd$Primary.Type.new,fill = rainbow(length(Type)), horiz=TRUE,cex = 0.6)

enter image description here

Upvotes: 1

Views: 1656

Answers (2)

hrbrmstr
hrbrmstr

Reputation: 78792

I hesitate to post this since this is an absolutely terrible use case for a pie chart, but it's possible to make it a bit more readable and color-blind friendly:

structure(c(ARSON = 833L, ASSAULT = 30743L, BATTERY = 91237L, 
BURGLARY = 29298L, `CRIMINAL DAMAGE` = 57539L, `CRIMINAL TRESPASS` = 14353L, 
`DECEPTIVE PRACTICE` = 17472L, HOMICIDE = 640L, KIDNAPPING = 517L, 
`MOTOR VEHICLE THEFT` = 23724L, NARCOTOCS = 55685L, `OFFENSE INVOLVING CHILDREN` = 3347L, 
`OTHER OFFENSE` = 30878L, `PUBLIC OFFENSE` = 3833L, `PUBLIC PEACE VIOLATION` = 3632L, 
ROBBERY = 18891L, `SEX CRIME` = 9331L, THEFT = 103255L, `WEAPONS VIOLATION` = 4792L
), .Dim = 19L, .Dimnames = list(. = c("ARSON", "ASSAULT", "BATTERY", 
"BURGLARY", "CRIMINAL DAMAGE", "CRIMINAL TRESPASS", "DECEPTIVE PRACTICE", 
"HOMICIDE", "KIDNAPPING", "MOTOR VEHICLE THEFT", "NARCOTOCS", 
"OFFENSE INVOLVING CHILDREN", "OTHER OFFENSE", "PUBLIC OFFENSE", 
"PUBLIC PEACE VIOLATION", "ROBBERY", "SEX CRIME", "THEFT", "WEAPONS VIOLATION"
)), class = "table") -> Type

Order the slices (IMPORTANT):

Type <- sort(Type, decreasing = TRUE) 

Proper % and decent labels:

piepercent <- scales::percent(as.numeric(Type/sum(Type)))

Margins:

par(mar = c(1, 1, 1, 1)) # bltr

pie(
  Type, 
  edges = 200, 
  radius = 0.8,
  clockwise = TRUE, # IMPORTANT
  angle = 45, 
  col = viridis::viridis_pal(option = "magma", direction=-1)(length(Type)),  # BETTER COLOR PALETTE
  labels = tail(piepercent, -7), # NEVER DISPLAY OVERLAPPING LABELS
  cex = 0.7
)

legend(
  x = 1.2, # DELIBERATE POSITION
  y = 0.5, # DELIBERATE POSITION
  inset = .05, 
  title = "Primary Crime Type", 
  legend = names(Type), # YOU WERE PASSING IN _ALL_ THE REPEAT NAMES
  fill = viridis::viridis_pal(option = "magma", direction=-1)(length(Type)),  # USE THE SAME COLOR PALETTE
  horiz = FALSE,
  cex = 0.6, # PROPER PARAMETER FOR TEXT SIZE
  text.width = 0.7 # SET THE BOX WIDTH
)

Add the title manually:

title("Pie Chart of Primary Crime Types", line = -1)

enter image description here

Upvotes: 2

hrbrmstr
hrbrmstr

Reputation: 78792

Can't let a pie chart stand alone (and, now, a 3D one at that):

structure(list(cat = c("Arson", "Assault", "Battery", "Burglary", 
"Criminal Damage", "Criminal Trespass", "Deceptive Practice", 
"Homicide", "Kidnapping", "Motor Vehicle Theft", "Narcotocs", 
"Offense Involving Children", "Other Offense", "Public Offense", 
"Public Peace Violation", "Robbery", "Sex Crime", "Theft", "Weapons Violation"
), val = c(833, 30743, 91237, 29298, 57539, 14353, 17472, 640, 
517, 23724, 55685, 3347, 30878, 3833, 3632, 18891, 9331, 103255, 
4792), pct = c(0.001666, 0.061486, 0.182474, 0.058596, 0.115078, 
0.028706, 0.034944, 0.00128, 0.001034, 0.047448, 0.11137, 0.006694, 
0.061756, 0.007666, 0.007264, 0.037782, 0.018662, 0.20651, 0.009584
)), class = "data.frame", row.names = c(NA, -19L)) -> xdf

dplyr::arrange(xdf, pct) %>% 
  dplyr::mutate(cat = factor(cat, levels=cat)) %>% 
  dplyr::mutate(lab = sprintf("%s (%s)", scales::comma(val), scales::percent(pct))) %>% 
  ggplot(aes(pct, cat)) +
  geom_segment(aes(xend=0, yend=cat), size=4, color = "#617a89") +
  geom_label(
    aes(label=lab), label.size = 0, hjust=0, nudge_x=0.001, 
    size = 3, family = hrbrthemes::font_rc, color = "#909495"
  ) +
  hrbrthemes::scale_x_percent(expand=c(0,0.001), limits=c(0,0.25)) +
  labs(x = NULL, y = NULL, title = "'Theft', 'Battery' & 'Criminal Damage' Account\nfor Half of Primary Recorded Crime Types") +
  hrbrthemes::theme_ipsum_rc(grid="X") +
  theme(axis.text.x = element_blank())

enter image description here

How I got xdf:

readLines(textConnection("ARSON                    ASSAULT                    BATTERY                   BURGLARY 
833                      30743                      91237                      29298 
CRIMINAL_DAMAGE          CRIMINAL_TRESPASS         DECEPTIVE_PRACTICE                   HOMICIDE 
57539                      14353                      17472                        640 
KIDNAPPING        MOTOR_VEHICLE_THEFT                  NARCOTOCS OFFENSE_INVOLVING_CHILDREN 
517                      23724                      55685                       3347 
OTHER_OFFENSE             PUBLIC_OFFENSE     PUBLIC_PEACE_VIOLATION                    ROBBERY 
30878                       3833                       3632                      18891 
SEX_CRIME                      THEFT          WEAPONS_VIOLATION 
9331                     103255                       4792")) %>% 
  trimws() %>%
  stri_split_regex("[[:space:]]+")  -> x

do.call(rbind.data.frame, lapply(seq.int(1, length(x), 2), function(i) {
  data.frame(
    cat = stri_trans_totitle(gsub("_", " ", x[[i]])), 
    val = as.numeric(x[[i+1]]), 
    stringsAsFactors = FALSE
  )
})) %>% 
  mutate(pct = val/sum(val)) -> xdf

Upvotes: 2

Related Questions