Connecting two points with geom_linerange()

Hello all,

I'm trying to make a graph to highlight the differences in proportions on several factors, between two conditions. Originally I made a side-by-side column chart, but then I found the graphs on this page and thought they would be more easily digestible. But I'm having trouble making the geom_linerange() part.

My data is here:

tribble(
~Factor,                     ~Perc_struc, ~Perc_unstruc,  ~diff,
Influencer,                         26,           52,    26,
Friends_relatives,                  33,           10,    23,
Young age,                          85,           66,    19,
Organized crime,                    67,           86,    19,
Actual distribution,                89,           72,    17,
Special skills,                     22,           38,    16,
Special zone,                       22,           38,    16,
Destroy evidence or hinder,         78,           66,    12,
Influenced by another,              22,           34,    12

My failed graph code, which works if you delete the linerange() part, is here:

considered_rankings_bigDiff |> 
  pivot_longer(cols = c("Perc_struc", "Perc_unstruc"), names_to = "Group", values_to = "Percent") |> 
  
  ggplot() +
  geom_point(aes(x=Factor, y=Percent, color=Group)) +
  geom_linerange(aes(ymin= Percent, ymax= Percent)) +
  #geom_linerange(data=considered_rankings_bigDiff, aes(ymin= Perc_struc, ymax= Perc_unstruc)) +
  theme_classic() +
  #scale_fill_manual(values=c("#FFC107", "#E91E63"), labels=c("Semi-structured", "Unstructured")) + 
  labs(title = "Differences in Consideration of Factors Across Conditions") +
  scale_x_discrete(labels=function(x) stringr::str_wrap(x, width = 10)) +
  theme(legend.title=element_blank()) 

First, you need to add x=Factor to the aesthetic argument in geom_linerange. That gets rid of an error message. That said, I think your use of pivot_longer may preclude getting what you want. If you look at the question you linked, the author says that it doesn't work with long data, for a reason that applies to your case. I think you might want a data frame with columns Factor, Group (structured or unstructured) and Percent (give or take the name Group, which you can obviously change to something more pertinent).

1 Like

Preclude may be too strong: The longer version is good for the point layer, and the point-range layer can get the wider version, similar to the solutions given in the linked question.

1 Like

Thanks! With some tweaking, that did fix it....

considered_bigDiff_plot=considered_rankings_bigDiff |> 
  mutate(Factor=factor(Factor, levels=c("Influencer", "Friends-relatives", "Young age",
                                        "Organized crime", "Actual distribution", "Special skills",
                                        "Special zone", "Destroy evidence or hinder", "Influenced by another"))) |> 
  ggplot() +
  geom_point(aes(x=Factor, y=Perc_struc), color="green") +
  geom_point(aes(x=Factor, y=Perc_unstruc), color="#ff00ff") +
  geom_linerange(aes(x=Factor, ymin=Perc_unstruc, ymax=Perc_struc), color="black") +
  
  # theme elements and settings
  theme_classic() +
  labs(title = "Differences in Consideration of Factors Across Conditions", y="Percent Considered") +
  scale_x_discrete(labels=function(x) stringr::str_wrap(x, width = 10)) +
  coord_cartesian(ylim = c(0,100))+
  
  # text labels for data 
  geom_text(aes(label = Perc_struc, x=Factor, y=Perc_struc), color="black", size = 4, vjust= 0, hjust=1.5) +
  geom_text(aes(label = Perc_unstruc,x=Factor, y=Perc_unstruc), color="black", size = 4, vjust= 0, hjust=1.5)

Any idea how to add a custom legend though? I can't seem to get that to work.

Ok, brace yourself. :slight_smile: Following the suggestion from @dromano , we'll use two data frames to generate the plot, one for the points and one for the lines.

To save myself typing, I renamed your original data frame to df in what follows.

df2 <- df |>  
  pivot_longer(cols = c("Perc_struc", "Perc_unstruc"), names_to = "Group", values_to = "Percent") |>
    mutate(Group = as.factor(Group)) |>
      mutate(Group = fct_recode(Group, "Structured" = "Perc_struc", "Unstructured" = "Perc_unstruc"))

df3 <- df |> 
  mutate(Factor=factor(Factor, levels=c("Influencer", "Friends-relatives", "Young age",
                                        "Organized crime", "Actual distribution", "Special skills",
                                        "Special zone", "Destroy evidence or hinder", "Influenced by another")))
  
ggplot() +
  geom_point(data = df2, aes(x=Factor, y=Percent, color = Group)) +
  geom_linerange(data = df3, aes(x=Factor, ymin=Perc_unstruc, ymax=Perc_struc), color="black") +
  
  # theme elements and settings
  theme_classic() +
  labs(title = "Differences in Consideration of Factors Across Conditions", y="Percent Considered") +
  scale_x_discrete(labels=function(x) stringr::str_wrap(x, width = 10)) +
  coord_cartesian(ylim = c(0,100))+
  scale_color_manual(values = c('Structured' = "green", "Unstructured" = "#ff00ff")) +
  
  # text labels for data 
  geom_text(data = df3, aes(label = Perc_struc, x=Factor, y=Perc_struc), color="black", size = 4, vjust= 0, hjust=1.5) +
  geom_text(data = df3, aes(label = Perc_unstruc,x=Factor, y=Perc_unstruc), color="black", size = 4, vjust= 0, hjust=1.5)

2 Likes

Absolutely brilliant! Thank you so much!!