众所周知,TikZ 必须面对 TeX 编译器给出的内存限制。
对于我的大多数图表,最大时间步长约为 1us,并且我在几秒钟内记录了几个波形,这导致了固有的内存问题。
我正在考虑使用 gnuplot 和 tikz,以便用 tikz 呈现轴,并且绘图将是我决定的大小的光栅图像。
有什么最佳实践吗?
(我将在今天结束前或周末发布一个最简单的例子)
评论我认为其中一种解决方法是将数据点栅格化,并以矢量图形绘制图例、轴等。理想情况下,栅格应该在绘图内制作,以保证其正确大小。我知道它的一部分matlab2tikz 2.0
发布
答案1
在处理这样的大矢量数据时,我非常担心出现(未被发现的)的可能性视觉混叠例如,考虑周期为 10(任意单位)的正弦信号,其噪声周期为 0.11。
#! /usr/bin/env python3
#
import math
import numpy as np
import scipy as sp
t1 = np.arange(0.0, 100.0, 1e-3)
y1 = np.sin(2*math.pi*t1/10) + 0.2*np.sin(2*math.pi*t1/0.11)
raw = np.column_stack((t1, y1))
np.savetxt("rawdata.dat", raw)
数据在文件中rawdata.dat
,您有 100000 个点。
pgfplots
会给你一个“TeX 容量超出”但你可以用以下方法绘制它:
\documentclass[border=10pt]{standalone}
\usepackage{tikz}
\usepackage{pgfplots}\pgfplotsset{compat=1.13}
\usetikzlibrary{arrows.meta,positioning,calc}
\begin{document}
\begin{tikzpicture}[
]
\begin{axis}[
xmin=0, xmax=100,
ymin=-1.5, ymax=1.5,
axis x line = center,
axis y line = center,
axis line style = {thick, gray},
xlabel = {$x$},
% every axis x label/.append style = {below, gray},
ylabel = {$y$},
legend style = {nodes=right},
legend pos = north east,
clip mode = individual,
]
\addplot[blue] table [x index=0, y index=1, each nth point={100}] {rawdata.dat};
\end{axis}
\end{tikzpicture}
\end{document}
使用该each nth point
功能。您将获得:
...这完全是错误的。噪声的周期似乎是真实周期的 10 倍;真实周期在以下gnuplot
图表中可见:
您可以看到错误来自何处。任何类型的子采样都必须小心执行以避免这种情况。
我通常会对数据进行预处理,并找出要抽取的每一片样本的平均值、最大值和最小值(将这段代码添加到上面的python
脚本中):
SAMPLE=100
np.savetxt("sampledata.dat", raw[0::SAMPLE, :])
#
# create the file with t, y, ymin, ymax
#
reducedlen = math.floor(len(t1)/SAMPLE)
reduced = np.zeros([reducedlen, 4])
for i in range(0, reducedlen):
j = i*SAMPLE
reduced[i, 0] = t1[j]
reduced[i, 1] = np.average(y1[j:j+SAMPLE])
reduced[i, 2] = np.min(y1[j:j+SAMPLE])
reduced[i, 3] = np.max(y1[j:j+SAMPLE])
np.savetxt("reduced.dat", reduced)
然后我滥用error bars
它们在平均信号周围形成一个“噪声带”(顺便提一句:您应该在此处使用更好的抗锯齿过滤器。平均值只是一个例子,有时可能会失败)。代码将是:
\addplot[red,
error bars/.cd,
y dir=both,
y explicit,
% error bar style={line width=2pt,}, % if you need it!
error mark options={
red,
mark size=0pt,
}
]
table [x index=0, y index=1, header = false,
y error minus expr = \thisrowno{1}-\thisrowno{2},
y error plus expr = \thisrowno{3}-\thisrowno{1},
]{reduced.dat};
结果如下——可能不是很好,但它是安全的。
fill between
顺便说一句,使用最小值和最大值也可以获得相同的图表,这可能更合乎逻辑:
\documentclass[border=10pt]{standalone}
\usepackage{tikz}
\usepackage{pgfplots}\pgfplotsset{compat=1.13}
\usetikzlibrary{arrows.meta,positioning,calc}
\usepgfplotslibrary{fillbetween}
\begin{document}
\begin{tikzpicture}[
]
\begin{axis}[
xmin=0, xmax=100,
ymin=-1.5, ymax=1.5,
axis x line = center,
axis y line = center,
axis line style = {thick, gray},
xlabel = {$x$},
% every axis x label/.append style = {below, gray},
ylabel = {$y$},
legend style = {nodes=right},
legend pos = north east,
clip mode = individual,
]
\addplot[red, name path = minimum]
table [x index=0, y index=2, header=false]{reduced.dat};
\addplot[red, name path = maximum]
table [x index=0, y index=3, header=false]{reduced.dat};
\addplot[red] fill between [of=minimum and maximum];
\end{axis}
\end{tikzpicture}
\end{document}
请注意,视觉混叠可以如果您使用全套数据,也会发生超出您控制范围的情况:在打印机中、在 PDF 查看器中等(它们应该具有内置的抗锯齿滤波器,但是——我更喜欢首先提供好的数据)。
答案2
光栅图像是一个好主意(可能最适合这种用例,矢量图像太大了)。
关键点是:您能否使用确定的边界框导出光栅图像,即您始终知道左下角的轴坐标和右上角的轴坐标?
如果是这样,您可以使用pgfplots
它的\addplot graphics
功能。手册中显示的第一个示例pgfplots
是
\begin{tikzpicture}
\begin{axis}[enlargelimits=false,axis on top]
\addplot graphics
[xmin=-3,xmax=3,ymin=-3,ymax=3]
{external1};
\end{axis}
\end{tikzpicture}
假设这external1
是一个具有紧密边界框的(光栅)图像,并且提供的限制分别是左下角和右上角的限制。
然而,生成具有明确边界框的图像并不像乍一看那么简单:许多程序会生成人工空间,而时间序列的 y 范围可能会影响导出图像的高度。
为了实现“开销小”的最佳实践,您可能需要编写一个代码生成器,它既生成外部图形,又生成相关的轴限值。在最简单的情况下,相关的轴限值是固定的,图像将始终适合。
参考文献: pgfplots 手册和相关问题pgfplots 中的三维直方图