在ggplot2中使用free_x重新排序facet_wrapped的x轴

12

我正在尝试在 ggplot2 中使用 scales = free_x 的 facet-wrapped 绘图中使用 reorder,但是 reorder 函数没有正确地重新排序 x 轴。这是我的代码:

library(ggplot2)

df <- read.table("speaking_distribution_by_play.txt",
                 header = F,
                 sep = "\t")

ggplot(df, aes(x=reorder(V2, V3), y=V3)) + 
  geom_bar(stat = "identity") +
  facet_wrap(~V1, ncol = 4, scales = "free_x") + 
  opts(title = "Distribution of Speakers in Shakespearean Drama") + 
  xlab("Speaking Role") + 
  ylab("Words Spoken") +
  opts(axis.text.x=theme_text(angle=90, hjust=1))

这个以制表符分隔的文件读取数据框并运行代码,可以得到一个图形,其中每个分面图的x轴仅部分排序。 SO上的其他人提出了一个非常类似的问题,但唯一的解决方案是使用网格排列。 由于我的数据集比那个问题中的数据集大得多,因此这不会是一个非常快速的操作,所以我想问:有没有办法重新排序每个分面图的x轴,以按大小递增(或递减)顺序显示条形图? 我将非常感谢其他人对这个问题提供的任何帮助。

1
哇... opts 自 2012 年以来已被弃用。我认为现在是时候更新你的 ggplot2 包了。你的第一个带有标题的 opts() 可以被替换为 labs()(并且你的 xlabylab 可以放在里面),而你的第二个 opts 将被替换为 theme() - Gregor Thomas
谢谢,Gregor!你对重新排序的问题有什么想法吗? - duhaime
1
如果你想在不同的方面中有不同的顺序,我认为 grid.arrange 是最好的选择。 - Gregor Thomas
1
+1 表示您提供了数据。大多数人都没有... - jlhoward
2个回答

14

通过略微不同的方法,您可以将标签保留在图表下方的区域。此版本通过以类似于jlhoward方法的方式将V1和V2连接起来创建唯一的x断点,然后在scale_x_discrete语句中使用代码中的roles函数将V2还原为x标签。

library(ggplot2)
df <- read.table("speaking_distribution_by_play.txt",
             header = F,
             sep = "\t")

# Creates a small test subset; remove for complete set 
df <- df[df$V1 %in% c("Mac.xml","MM.xml","MND.xml","MV.xml"),]

# used to create x-axis label restoring original name of role
roles <- function(x) sub("[^_]*_","",x )   

ggplot(cbind(df, V4=paste(df$V1,df$V2,sep="_")), aes(x=reorder(V4,V3), y=V3) ) + 
geom_bar(stat = "identity") +
facet_wrap(~ V1,  ncol=4, scales = "free_x") +
labs(title = "Distribution of Speakers in Shakespearean Drama") + 
xlab("Speaking Role") + 
ylab("Words Spoken") +
scale_x_discrete(labels=roles) +
theme(axis.text.x=element_text(angle=90, hjust=1)) 

enter image description here


2
很高兴能帮到你。我注意到我需要对角色函数进行小的更改,以正确处理所有V1的值。上面的编辑版本已经修复了这个问题。对此很抱歉。 - WaltS
我希望我能给这个点赞两次。这刚刚解决了我几个小时的问题。 - Nick Criswell
哇!这是一个非常棒的解决方案! - Juanchi
谢谢,真的很有帮助! - crsh

7
问题是ggplotV2作为单个因子处理;它不会为每个分面(V1的值)对V2进行子集划分,然后将每个子集视为独立的因子(遗憾的是)。由于有些角色("Messenger 1"等)出现在多个剧本中,这些级别基于它们在第一个遇到它们的剧本中的重要性进行排序。
有一个解决方法,但它有点像黑客:您需要通过将剧本名称连接到每个角色上使其唯一,然后使用该名称作为x值。为了恢复原始角色,请关闭轴文本,并使用geom_text(...)作为条形图标签。以下是一个示例:
gg     <- df[order(df$V1,-df$V3),]   # reorder by play and lines
gg$lvl <- with(df,paste(V2,V1,sep="."))

ggplot(gg[gg$V1 %in% unique(df$V1)[1:4],], 
       aes(x=factor(lvl,levels=unique(lvl)), y=V3)) + 
  geom_text(aes(y=5,label=V2),angle=90,size=3,hjust=-0)+
  geom_bar(stat = "identity", fill="blue",alpha=0.2) +
  facet_wrap(~V1, ncol = 2, scales="free_x") + 
  labs(title="Distribution of Speakers in Shakespearean Drama", 
       x="Speaking Role", y="Words Spoken") +
  theme(axis.text.x=element_blank(),axis.ticks.x=element_blank())

这个图在如此小的尺寸下看起来很糟糕(虽然不像你原来的图那么糟糕...)。但是如果你把它放大(因为你必须处理38个剧本,对吗?),那么你就可以看到标签和条形图。如果你真的想要标签在条形图下面,使用类似于以下的方法:

ggplot(gg[gg$V1 %in% unique(df$V1)[1:4],], 
       aes(x=factor(lvl,levels=unique(lvl)), y=V3)) + 
  geom_text(aes(y=-5,label=V2),angle=90,size=3,hjust=1)+
  ylim(-500,NA)+
  geom_bar(stat = "identity", fill="lightblue") +
  facet_wrap(~V1, ncol = 2, scales="free_x") + 
  labs(title="Distribution of Speakers in Shakespearean Drama", 
       x="Speaking Role", y="Words Spoken") +
  theme(axis.text.x=element_blank(),axis.ticks.x=element_blank())

再次强调,这个小尺寸下看起来很糟糕,但放大后会好些。无论如何,您都可能需要调整geom_text(...)中的size=...参数。


1
聪明的技巧!热情洋溢 +1 - Gregor Thomas
你是一位学者,@jlhoward!非常感谢你提供的这个巧妙的解决方法 :) - duhaime

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接