目录
前言
本文来源于官方文档的整理,由于英语水平有限,如有错误请在评论区指出。
Plots.jl 实际上并不是一个绘图包!Plots.jl 是一个绘图元包:它是许多不同绘图库的接口。因此,Plots.jl 实际所做的是解释您的命令,然后使用另一个绘图库生成绘图。后台的这个绘图库称为后端。这样做的好处是,这意味着您可以使用 Plots.jl 语法使用许多不同的绘图库。
一、基本绘图
1. 线图
using Plots
x = 0:10; y = sin.(x); # These are the plotting data
plot(x, y)
using Plots
x = 0:10; y1 = sin.(x); y2 = cos.(x) # 绘图数据
p1 = plot(x, y1) # 图1
p2 = plot(x, [y1 y2]) # 图2
p3 = plot!(x, x.+1) # 图3,在图2基础上增加图形
plot(p1,p2,p3;layout = 3, label=["a" "b" "c"],title=["figure1" "figure2" "figure3"])
2. 散点图
scatter(x, y, title = "My Scatter Plot")
scatter([1:50], [y1 y2], framestyle=:box,mc=[:blue :green])
3. 条形图
3.1 基本条形图
p1 = bar([1,2,3])
p2 = bar([1,2,3];bar_width=0.5) # 设置条的宽度
p3 = bar([1,2,3];fc=[1,2,3],ms =10) # 填充颜色
p4 = bar([1,2,3];fc=[1,2,3],lc=:white) # 填充颜色及边的颜色
plot(p1,p2,p3,p4;layout = 4, label=["a" "b" "c" "d"],title=["figure1" "figure2" "figure3" "figure4"])
3.2 堆积条形图
p1 = bar([3 2 1;6 5 4]) # 后面的值覆盖在前面的值之上
p2 = bar([3 2 1;6 5 4];dir=:h ) # 改变图的方向
p3 = bar([3 2 1;6 5 4],axiscolor=:red) # 坐标轴颜色
p4 = bar([3 2 1;6 5 4],bordercolor=:white) # 边的颜色
plot(p1,p2,p3,p4;layout = 4, label=["a" "b" "c" "d"],title=["figure1" "figure2" "figure3" "figure4"])
4. 保存图片
using Plots
p = plot(rand(100))
png(p, filename) # 将绘图保存为png文件
pdf(p, filename) # 将绘图保存为pdf文件
svg(p, filename) # 将绘图保存为svg文件
eps(p, filename) # 将绘图保存为eps文件
ps(p, filename) # 将绘图保存为ps文件
json(p, filename) # 将绘图保存为json文件
tex(p, filename) # 将绘图保存为tex文件
txt(p, filename) # 将绘图保存为txt文件
save(p,filename) # 根据文件后缀名推断文件类型并保存
5. 综合案例
using Plots
# 准备数据
x=collect(1:20)
y1=rand(20)
y2=rand(20)
# 提前设置各种属性
default(
titlefont = (20, "times"),
legendfontsize = 18,
guidefont = (18, :darkgreen),
tickfont = (12, :orange),
guide = "x",
title = "Example",
framestyle = :zerolines,
yminorgrid = true,
legend = :outertopleft
)
# 进行绘图
p=plot([x x],[y1 y2], label = ["line1" "line"], xlabel = "X", linewidth = 2)
# 保存图片
png(p,"test.png")
二、基本属性
在 Plots.jl 中,绘图的修饰符称为 attributes,这些都记录在属性页。Plots.jl 数据与属性遵循的规则是:位置参数是数据,关键字参数是属性。例如:plot(x,y,attribute=value)。
从属性页面中看到,linewidth
可以用于(或其别名lw
)增加线宽,使用命令label
更改图例的标签,并使用 title
添加标题。让我们将其应用于我们之前的情节:
x = 1:10; y = rand(10, 2) # 2列意味着两条线
plot(x, y, title = "Two Lines", label = ["Line 1" "Line 2"], lw = 3)
每个属性也可以通过使用修饰函数改变绘图来应用。例如,利用函数 xlabel!
为 x 轴添加标签
xlabel!("My x label")
在绘图前设置好默认属性
# 设置默认字体
default(
titlefont = (20, "times"),
legendfontsize = 18,
guidefont = (18, :darkgreen),
tickfont = (12, :orange),
guide = "x",
framestyle = :zerolines,
yminorgrid = true)
# 进行绘图
plot([sin, cos], -2π, 2π, label = ["sin(θ)" "cos(θ)"], title = "Trigonometric Functions", xlabel = "θ", linewidth = 2, legend = :outertopleft)
0. 常用属性
属性名 | 作用 | 说明 |
---|---|---|
size | 图片大小 | 默认(600, 400) |
dpi | 分辨率 | dpi=100,默认100 |
title | 标题 | title = "自定义标题" |
titlefontsize | 标题大小 | titlefontsize = 10 |
label | 图例 | label= ["图例一" "图例二"],可为空 |
xlabel | X轴 | xlabel = "自定义x轴名称" |
ylabel | Y轴 | ylabel = "自定义y轴名称" |
xticks | X轴刻度 | xticks = 0:1:10 |
yticks | Y轴刻度 | yticks = 0:10:100 |
xlims | X轴范围 | xlims = (0,10) |
ylims | Y轴范围 | ylims = (0,100) |
guidefontsize | 轴标签字体大小 | guidefontsize = 10 |
grid | 网格 | true, false, :x, :y, :z |
framestyle | 轴框架的样式 | :axes, :box, :semi, :origin, :zerolines, :grid, :none |
linewidth | 线条粗细 | lw = 4 |
linecolor, lc | 线条颜色 | lc=[:blue :green] |
linestyle, ls | 线条风格 | :auto, :solid, :dash, :dot, :dashdot, :dashdotdot |
markercolor | 点标志颜色 | mc=[:orange :purple] |
markershape | 点标志形状 | :circle, :rect, :star4-8, :diamond, :hexagon, :cross, :xcross, , , :vline, :hline, :+, :x等 |
markersize, ms | 点标志大小 | ms=5 |
fillcolor, fc | 填充颜色 | fc = :red |
background | 背景颜色 | bg = :white |
background_outside | 外部背景色 | bgoutside = :gray90 |
fontfamily | 字体样式 | serif 、sans-serif、 monospace |
annotations | 注释 | annotations = (-1, 0,"this is #10", :right, 20, "courier") |
smooth | 是否添加回归线 | 默认false |
1. 轴
将设置元组传递到xaxis参数将快速定义xlabel、xlims、xticks、xscale、xflip和xtickfont。以下是等效的:
plot(y, xaxis = ("my label", (0,10), 0:0.5:10, :log, :flip, font(20, "Courier")))
plot(y,
xlabel = "my label", # x轴的标签
xlims = (0,10), # x轴的取值范围
xticks = 0:0.5:10, # x轴的刻度
xscale = :log, # x轴的尺度类型
xflip = true, # 是否翻转图形
xtickfont = font(20, "Courier") # x轴刻度的字号及字体
)
plot!(xticks = ([0:π:3*π;], ["0", "\\pi", "2\\pi"])) # 将元组传递给xticks,改变刻度和标签的位置
yticks!([-1:1:1;], ["min", "zero", "max"]) # 更改yticks
2. 线
设置与系列:线对应的属性。别名:。以下是等价的:
plot(y, line = (:steppre, :dot, :arrow, 0.5, 4, :red))
plot(y,
seriestype = :steppre, # 线的类型
linestyle = :dot, # 线的样式
arrow = :arrow, # 线末尾的形状箭头
linealpha = 0.5, # 线的不透明度
linewidth = 4, # 线的宽度
linecolor = :red # 线的颜色
)
3. 填充
设置与系列填充区域对应的属性。别名:f
, area
. 以下是等价的:
plot(y, fill = (0, 0.5, :red))
plot(y,
fillrange = 0, #填充范围和y之间的区域
fillalpha = 0.5, # 填充区域的不透明度
fillcolor = :red # 填充颜色
)
4. 标记
设置与系列标记对应的属性。别名:m
, mark
. 以下是等价的:
scatter(y, marker = (:hexagon, 20, 0.6, :green, stroke(3, 0.2, :black, :dot)))
scatter(y,
markershape = :hexagon, # 标记形状
markersize = 20, # 标记大小
markeralpha = 0.6, # 标记的不透明度
markercolor = :green, # 标记的颜色
markerstrokewidth = 3, # 标记边界宽度
markerstrokealpha = 0.2, # 标记边界的不透明度
markerstrokecolor = :black, # 标记边界的颜色
markerstrokestyle = :dot # 标记边界的样式
)
5. 注释
可通过 annotations属性 或者 annotate!()函数 向图像添加注释文本。
using Plots
plot(
1:10,
annotations = (-1, 0, Plots.text("this is #3", :left,50,:gray,rotation=45.0)),
)
# 两组数据
annotate!([(7,3,"(1,1)"),(3,7,text("hey", 14, :left, :top, :green))])
# 传入一组数据
annotate!([(4, 4, ("More text", 8, 45.0, :bottom, :red))])
# 传入两组数据
annotate!([2,5], [6,3], ["text at (2,6)", "text at (5,3)"])
6. 字体
font()函数 可从一个特征列表中创建一个Font。值可以被指定为参数(通过类型/值来区分)或关键字参数。具体参数如下
family
: AbstractString. "serif" or "sans-serif" or "monospace"pointsize
: Integer. 字体的大小halign
: Symbol. 水平排列方式 (:hcenter, :left, or :right)valign
: Symbol. 垂直排列方式 (:vcenter, :top, or :bottom)rotation
: Real.文本旋转角度,单位为度 (use a non-integer type)color
: Colorant or Symbol
julia> font(8)
julia> font(family="serif", halign=:center, rotation=45.0)
7. 间距
plot(
1:10,
top_margin=5mm, # 上间距
left_margin=10mm, # 左间距
right_margin = 30mm, # 右间距
bottom_margin = 20mm, # 下间距
)
8. 其他
scatter(y, thickness_scaling = 2)
# 将字体大小和线宽增加2倍
# 如果后端不支持这个,请使用函数`scalefontsizes(2)`,它可以缩放默认字体
scatter(y, ticks=:native)
# 让后端自行计算刻度线。
# 使用交互式后端进行鼠标缩放时,这将非常有用
scatter(rand(100), smooth=true)
# 在图中添加回归线
三、布局设置
- Plot: 整个图形/窗口。
- Subplot: 一个子图包含:标题(title)、轴(axes)、色栏(colorbar)、图例(legend)和图形区域(plot area)。
- Axis: 子图的一个轴,包含轴向导(标签)、记号标签和记号标记。
- Plot Area: 数据所示的一个子图的部分……包含了序列、网格线等。
- Series: 一种特定的数据可视化。( 例如 : a line或一组标记 )
1. 简单布局
1.1 将整数传递给layout,使其能够自动计算许多子图的网格大小:
# 创建2x2网格,并将4个系列中的每个映射到其中一个子图
plot(rand(100, 4), layout = 4)
1.2 将元组传递给layout以创建该大小的网格:
# 创建一个4x1网格,并将4个系列的每个映射到其中一个子图
plot(rand(100, 4), layout = (4, 1))
1.3 使用grid(...) 构造函数创建更复杂的网格布局:
# heights数组为各子图的高度
plot(rand(100, 4), layout = grid(4, 1, heights=[0.1 ,0.4, 0.4, 0.1]))
1.4 添加标题(title)及标签(label)
plot(rand(100,4), layout = 4, label=["a" "b" "c" "d"],
title=["1" "2" "3" "4"])
1.5 逐步添加子图
将多个图组合成一个图,需将保存先前图的变量传递给plot
函数:
l = @layout [a ; b c]
p1 = plot(...)
p2 = plot(...)
p3 = plot(...)
plot(p1, p2, p3, layout = l)
2. 高级布局
@layout
宏是定义复杂布局最简单的方法,使用 Julia 的多维数组自定义布局为基础,还可以使用大括号可以实现精确的大小调整,否则子图在可用空间区域进行平均分配。
符号本身(在下面a
的b
示例中)可以是任何有效的标识符,并且没有任何特殊含义。
l = @layout [a{0.3w} [grid(3,3) b{0.2h}]]
plot(
rand(10, 11),
layout = l, legend = false, seriestype = [:bar :scatter :path],
title = ["($i)" for j in 1:1, i in 1:11], titleloc = :right, titlefont = font(8)
)
四、其他用法
1. 主题选择
通过函数 theme(主题样式名) 可设置置图形的主题。例如:
theme(:default)
主题的示例可见:网址,可使用的主题如下表:
:default | :orange | :wong2 | :mute |
:dark | :sand | :gruvbox_dark | :dao |
:ggplot2 | :solarized | :gruvbox_light | :dracula |
:juno | :solarized_light | :bright | |
:lime | :wong | :vibrant |
2. 颜色
有许多颜色属性,用于lines、fills、markers、backgrounds和foregrounds。许多颜色都遵循一个层次结构......例如,lineecolor从seriescolor获得它的值,除非你覆盖了这个值。这使得你可以简单地精确设置你想要的东西,而不需要大量的模板。
支持的类型:
- 命名颜色,如—— :red,:white等
- 颜色字符串,如—— "red", "white"等
- 十六进制颜色,如——"#4472c4"等
- RGB颜色,如—— "rgb(25,202,173)"等
- 十进制整数
说明:
- 符号或字符串将被传递给Colors.parse(Colorant, c),所以:red相当于colorant "red"
- false或者nothing将被转换为一个不可见的RGBA(0,0,0,0)
- 一个整数,它将从系列颜色中挑选出相应的颜色
2.1 命名颜色
支持的命名颜色见 网址,常用的颜色比如:red,blue,blue1, blue2, blue3, ...等等。
2.2 系列颜色
对于系列,有几个属性需要了解。
- seriescolor: 不直接使用,但定义了系列的基础颜色
- linecolor: 路径的颜色
- fillcolor: 填充区域颜色
- markercolor: 标记和形状的内部颜色
- markerstrokecolor: 标记和形状的边界/笔触的颜色
seriescolor默认为:auto,并根据其在子图中的索引从color_palette中分配一个颜色。默认情况下,其他颜色 :match。
(一般来说,颜色渐变可以通过*color来设置,而相应的颜色值在渐变中通过*_z来查询。)
这些属性中的每一个都有一个相应的alpha覆盖: seriesalpha、linealpha、fillalpha、markeralpha和markerstrokealpha。它们是可选的,你仍然可以作为Colors.RGBA的一部分给出alpha信息。
在某些情况下,当用户没有设置一个值时,线条颜色或标记笔画颜色可能被覆盖。
2.3 前景/背景(Foreground/Background)
前景色和背景色的工作原理是相似的。
2.4 用法说明
- 默认主题下的线条颜色不是CSS定义的,但接近于:steelblue。
- line_z和marker_z参数将把数据值映射为ColorGradient值。
- color_palette决定了当seriescolor == :auto时分配的颜色。
- 如果传递一个颜色的向量,它将强制循环这些颜色。
- 如果通过一个梯度,它将无限地从该梯度中提取独特的颜色,试图将它们分散开来。
2.5 色彩主题
Plots支持ColorSchemes.jl中的所有色系。它们可以作为梯度或调色板使用,并作为持有其名称的符号传递给cgrad或palette。
plot(
[x -> sin(x - a) for a in range(0, π / 2, length = 5)], 0, 2π;
palette = :Dark2_5,
)
2.6 ColorPalette(调色板)
Plots从传递给color_palette属性的调色板中自动为系列选择颜色。该属性接受colorscheme名称的符号或ColorPalette对象。调色板可以用 palette(cs, [n]) 构建,其中cs可以是一个符号、一个颜色向量、一个ColorScheme、ColorPalette或ColorGradient。可选参数n决定从cs中选择多少种颜色。
palette(:tab10)
palette([:purple, :green], 7)
palette([RGB(0,0,0), RGB(1,1,1)], 7)
2.7 ColorGradient(颜色梯度)
对于heatmap、surface、contour或line_z、marker_z和line_z Plots.jl从一个ColorGradient中选择颜色。如果没有指定,将使用默认的ColorGradient :inferno。通过向seriescolor属性传递一个colorscheme名称的符号,可以选择不同的梯度。对于更详细的配置,颜色属性也接受一个ColorGradient对象。颜色梯度可以通过以下方式构建
cgrad(cs, [z], alpha = nothing, rev = false, scale = nothing, categorical = nothing)
其中cs可以是一个符号、一个颜色向量、一个ColorScheme、ColorPalette或ColorGradient。
cgrad(:acton)
你可以传递一个介于0和1之间的数值向量作为第二个参数来指定颜色转换的位置。
cgrad([:orange, :blue], [0.1, 0.3, 0.8])
在rev = true的情况下,colorscheme的颜色被反转。
设置categorical = true会返回一个CategoricalColorGradient,它只从一个离散的颜色集合中选择,而不连续插值。可选的第二个参数决定了要从颜色表中选择多少种颜色。它们会沿着colorscheme的颜色均匀分布。
cgrad(:matter, 5, categorical = true)
3. 显示中文
使用默认字体无法正常显示中文,可设置 fontfamily 属性以显示中文,其中默认后端GR可设置的字体如下所示,详情可见Fonts — GR Framework 0.71.7 documentation
Times Roman | Helvetica | Courier | Symbol |
Times Italic | Helvetica Oblique | Courier Oblique | Bookman Light |
Times Bold | Helvetica Bold | Courier Bold | Bookman Light Italic |
Times Bold Italic | Helvetica Bold Oblique | Courier Bold Oblique | Bookman Demi |
4. 绘图后端
当我们开始在上面绘图时,我们的绘图使用了默认的后端 GR。但是,假设我们想要一个不同的绘图后端,它将绘图到一个漂亮的 GUI 或 VS Code 的绘图窗格中。为此,我们需要一个与这些功能兼容的后端。一些常见的后端是 PyPlot 和 Plotly。要安装这些后端,只需使用标准 Julia 安装 ( Pkg.add("BackendPackage")
)。我们可以通过使用全小写的后端名称作为函数来专门选择我们正在绘制的后端。
我们可以通过使用全小写的后端名称作为函数来专门选择我们正在绘制的后端。让我们使用 Plotly 和 GR 从上面绘制示例:
x = 1:10; y = rand(10, 2) # 2 columns means two lines
plotlyjs() # Set the backend to Plotly
# This plots into the web browser via Plotly
plot(x, y, title = "This is Plotted using Plotly")
gr() # Set the backend to GR
# This plots using GR
plot(x, y, title = "This is Plotted using GR")
如果您在 VS Code 或 Juno 中,第一个绘图命令将导致绘图在绘图窗格中打开。如果您在 REPL 中,绘图命令将在浏览器窗口中打开。无论如何,您始终可以使用该gui()
命令打开 GUI。
每个绘图后端都有非常不同的感觉。有些具有交互性,有些更快并且可以处理大量数据点,有些可以绘制 3D 绘图。保存绘图由savefig
命令完成。举个例子:
savefig("myplot.png") # Saves the CURRENT_PLOT as a .png
savefig(p, "myplot.pdf") # Saves the plot from p as a .pdf vector graphic
像 GR 这样的一些后端可以保存到矢量图形和 PDF,而像 Plotly 这样的其他后端只能保存到.png
。有关后端的更多信息,请参阅后端页面。有关来自各种后端的绘图示例,请参阅示例部分。
5. 绘图系列
在 Plots.jl 中,这些其他绘制系列的方式称为系列类型。支持的所有系列类型见:Supported Attributes · Plots
常见系列类型包含:bar(柱状图), scatter(散点图), scatter3d(3D散点图)
系列类型可以通过seriestype
属性进行更改,如下所示:
gr() # We will continue onward using the GR backend
plot(x, y, seriestype = :scatter, title = "My Scatter Plot")
对于每个内置系列类型,都有一个速记函数用于直接调用与系列类型名称匹配的系列类型。它处理与命令相同的属性plot
. 例如,我们可以改为使用以下方法绘制散点图:
scatter(x, y, title = "My Scatter Plot")
6. 脚本绘图
在脚本中,Julia 不会进行自动显示(这就是;
不需要的原因)。但是,如果我们想在脚本中显示我们的图,这意味着我们只需要添加display
调用。例如:
display(plot(x, y))
五、绘制动画
动画是通过以下3个步骤创建:
- 初始化一个
Animation
对象. - 用 fram(anim) 保存动画的每一帧。
- 用 gif(anim, filename, fps=15) 将帧转换成gif动画。
(方便的宏@gif和@animate极大地简化了这些代码。短版本的例子见主页,长版本的例子见gr。)
有两个宏为创建动画提供了不同程度的便利。@animate和@gif。主要区别在于,@animate将返回一个Animation对象供以后处理,而@gif将创建一个gif动画文件(并在返回到IJulia单元时显示)。
对于简单的、一次性的、你想立即观看的动画,使用@gif。对于任何更复杂的东西,请使用@animate。当你需要完全控制动画的生命周期时,可以构建动画对象(不过通常没有必要)。
using Plots
p = scatter([0,25,75,100],[0,1,-1,0])
@gif for i ∈ 1:100
x = i
y = sin((i/100)*2pi)
scatter!([x],[y],c = :reds,legend = false)
end
l = @layout([[a; b] c])
p = plot(plot([sin, cos], 1, ylims = (-1, 1), leg = false), scatter([atan, cos], 1, ylims = (-1, 1.5), leg = false), plot(log, 1, ylims = (0, 2), leg = false), layout = l, xlims = (1, 2π))
anim = Animation()
for x = range(1, stop = 2π, length = 20)
plot(push!(p, x, Float64[sin(x), cos(x), atan(x), cos(x), log(x)]))
frame(anim)
end
gif(anim, "test.gif")
using Plots
@userplot CirclePlot
@recipe function f(cp::CirclePlot)
x, y, i = cp.args
n = length(x)
inds = circshift(1:n, 1 - i)
linewidth --> range(0, 10, length = n)
seriesalpha --> range(0, 1, length = n)
aspect_ratio --> 1
label --> false
x[inds], y[inds]
end
n = 150
t = range(0, 2π, length = n)
x = sin.(t)
y = cos.(t)
anim = @animate for i ∈ 1:n
circleplot(x, y, i)
end
gif(anim, "anim_fps15.gif", fps = 15)
gif(anim, "anim_fps30.gif", fps = 30)
每一个标志只会在 "每N次迭代 "时保存一个框架。
@gif for i ∈ 1:n
circleplot(x, y, i, line_z = 1:n, cbar = false, framestyle = :zerolines)
end every 5
when标志只会在 "表达式为真时 "保存一个帧。
n = 400
t = range(0, 2π, length = n)
x = 16sin.(t).^3
y = 13cos.(t) .- 5cos.(2t) .- 2cos.(3t) .- cos.(4t)
@gif for i ∈ 1:n
circleplot(x, y, i, line_z = 1:n, cbar = false, c = :reds, framestyle = :none)
end when i > 40 && mod1(i, 10) == 5