如何用ggplot的facet_wrap对两个列表数据分面绘图?
问题描述
我有两个向量列表,一个存储188段旋律的半音数据,另一个存储每段旋律的时间线(以秒为单位)。希望用ggplot为每段旋律绘制图表,查看旋律随时间的变化趋势。用plot()绘制单个旋律正常,但不知道如何组织dataframe/tibble来实现分面绘图。
附上测试数据:
library(lubridate) melodylist<-list(c(11L, 4L, 11L, 12L, 11L, 7L, 9L, 11L, NA, 11L, 12L, 14L, 12L, 9L, 7L, 5L, 4L, 11L, 4L, 11L, 12L, 11L, 7L, 9L, 11L, NA, 11L, 12L, 14L, 12L, 9L, 7L, 5L, 4L, NA, 9L, 7L, 12L, 11L, 9L, 14L, 12L, 11L, 9L, NA, 12L, 11L, 12L, 14L, 7L, 11L, 12L, 9L, 7L, NA, 7L, 12L, 11L, 9L, 4L, 7L, 5L, 4L), c(9L, 7L, 9L, 12L, 14L, 12L, 11L, 9L, NA, 9L, 5L, 7L, 9L, 7L, 5L, 4L, 2L, 9L, 7L, 9L, 12L, 14L, 12L, 11L, 9L, NA, 9L, 5L, 7L, 9L, 7L, 5L, 4L, 2L, NA, 2L, 5L, 7L, 5L, 7L, 9L, NA, 9L, 14L, 12L, 14L, 16L, 12L, 11L, 9L, NA, 9L, 12L, 9L, 12L, 7L, 5L, 2L, 4L, 2L, 9L, 7L, 5L, 4L, 2L)) timelist<-list(new("Period", .Data = c(0, 2, 3, 4, 5, 6, 7, 8, 9, 9.5, 10, 11, 12, 13, 14, 15, 16, 17, 19, 20, 21, 22, 23, 24, 25, 26, 26.5, 27, 28, 29, 30, 31, 32, 33, 34, 34.5, 35, 36, 37, 38, 38.5, 39.5, 40, 41, 42, 42.5, 43, 44, 45, 46, 47, 47.5, 48, 49, 50, 50.5, 51, 52, 53, 54, 55, 56, 57), year = c(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), month = c(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), day = c(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), hour = c(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), minute = c(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)), new("Period", .Data = c(0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 14.5, 15, 16, 18, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 32.5, 33, 34, 36, 37, 38, 39, 40, 41, 42, 43, 43.5, 44, 45, 46, 47, 48, 49, 50, 51, 51.5, 52.5, 53.5, 54.5, 55.5, 56.5, 57, 57.5, 58.5, 59.5, 0.5, 1, 1.5, 2.5), year = c(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), month = c(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), day = c(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), hour = c(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), minute = c(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1)))
注:无需担心NA值,原本用humdrum格式输出为".",绘图时无问题。曾尝试将列表直接放入dataframe,但ggplot无法识别这种格式。
解决方案
要实现分面绘图,核心是把数据整理成**长格式(long format)**的dataframe,每一行对应一个时间点的音高数据,同时标记所属的旋律编号。
步骤1:加载必要的包
除了lubridate,还需要tidyverse(包含dplyr和ggplot2)来处理数据和绘图:
library(tidyverse) library(lubridate)
步骤2:将列表转换为长格式数据框
使用map2_df同时遍历两个列表,将每段旋律的时间、音高和编号组合成统一的数据框:
# 遍历旋律和时间列表,生成带旋律编号的长格式数据 melody_data <- map2_df(melodylist, timelist, function(mel, time) { tibble( time = as.numeric(time), # 将Period对象转换为数值型秒数 pitch = mel ) }, .id = "melody_id") # .id参数自动为每个旋律分配唯一标识
步骤3:用ggplot绘制分面图
使用facet_wrap()按旋律编号生成独立子图:
ggplot(melody_data, aes(x = time, y = pitch)) + geom_line() + # 绘制旋律折线 geom_point(na.rm = TRUE) + # 标记每个音高点(忽略NA) facet_wrap(~melody_id, ncol = 4) + # 按旋律分面,每行显示4个图表 labs(x = "时间(秒)", y = "半音音高") + theme_bw()
关键说明
as.numeric(time):把lubridate的Period对象转换为数值型秒数,确保ggplot能识别为连续变量。map2_df:来自purrr包,高效遍历两个配对列表并合并结果为数据框,替代手动循环。facet_wrap(~melody_id):自动为每个旋律生成独立子图,ncol参数可调整每行显示的子图数量,适配188段旋律的展示需求。
内容的提问来源于stack exchange,提问作者IMIAD
相关产品推荐
相关产品推荐

