edit (14/06): Thanks to the hint of marmot, and based on this answer. I define \markopts
in scatter/@pre marker code/.code
like this:
scatter/@pre marker code/.code={
\pgfmathtruncatemacro{\lasttwomarks}{%
or((\coordindex==(\numcoords-2)), (\coordindex==(\numcoords-1)))%
}
\ifnum\lasttwomarks=1
\def\markopts{mark=none}
\else
\def\markopts{mark=*,mark size=1pt}
\fi
\ifnum\coordindex=0
\def\markopts{mark=*,fill=white,mark size=2pt}
\fi
\expandafter\scope\expandafter[\markopts]
}
,scatter/@post marker code/.code={
\endscope
}
... which hides the last two marks and removes my need to append \draw
to the plot. I now just add two "dummy" coordinates to the data for the arrow end. See this gist.
original question:
I'd like to mimic this graph:
So far I've bee stuck on adding plots that start with a *
mark for the first datapoint that is not filled, filled marks for the other data points, and arrow ends that point somewhere approximately in the right direction. I know that pgfplots
treats coordinates given by \draw
as one would expect withing the axis environment, but I wounder if I can adjust my command so that it can be used there.
\documentclass[border=5mm,tikz]{standalone}
\usepackage{pgfplots}
\pgfplotsset{compat=1.15}
\usetikzlibrary{arrows.meta}
\begin{document}
\tikz\draw[{Circle[fill=none]}-Stealth,thick] (0, 0) --
plot[smooth,mark=*] coordinates {(1, .24) (2, .42) (3, .54) (4, .59)} -- (5, .6);
% \begin{tikzpicture}[baseline]
% \begin{axis}[xtick=\empty,xmin=-1,xmax=6,ymin=-1,ymax=1]
% \addplot+ [yshift=2pt,red,smooth,mark=*] coordinates {(1, .24) (2, .42) (3, .54) (4, .59) (5, .6)};
% \end{axis}
% \end{tikzpicture}
\end{document}
edit:
some background:
Basically, the graph represents the piano tuner's choice when applying stretched tuning: the bass strings are usually thick and have a lot more spectral components. Each plot represents the prominent partials in the harmonic series of a single recorded note from a 1923 Steinway piano. The aligned keyboard and the grid lines (12 notes per octave => xtick distance=12
) indicate to which note the partials are closest, and the y axis shows deviation in cents. The aural compromise chosen by the tuner often depends on the resonating partials in different pianos. I have measurements of a different piano that I would like to represent in such a graph.
The notes used are all (on different octaves) A's. The unfilled marks represent the fundamental pitch (the first value of a plot), the filled ones are prominent partials, and the pinned labels always point to intersecting plots.
In the meantime I wrote this:
\documentclass[border=5mm]{standalone}
\usepackage{pgfplots}
\usetikzlibrary{arrows.meta,plotmarks,decorations.markings,intersections}
\tikzset{%
every pin/.style={black,fill=white,rectangle,outer sep=1pt,inner sep=1pt, rounded corners=3pt,font=\tiny}%
,every mark/.append style={scale=.5}%
,n/.style={xshift=-1ex,yshift=1em,font=\bfseries}
}
\pgfplotsset{%
width=16cm%
,height=7cm%
,scale only axis%
,compat=1.15%
,major grid style={dotted,thin,gray}
,tick label style={font=\small}
,tickwidth=0pt
,label style={font=\small}
,axis x line=bottom
,axis y line=left
}
\begin{document}
\begin{tikzpicture}[baseline,pin distance=4ex]
\begin{axis}[%
name=main plot
,clip=false
,xtick distance=12
,xticklabels={0, A0, A1, A2, A3, A4, A5, A6, A7},
,ylabel={Deviation (cents)}
,ytick={-25,-20,...,45}
,ymin=-34
,ymax=50
,xmin=-4
,xmax=90
,grid=major
,major grid style={black!50}
,axis y discontinuity=crunch
,enlargelimits=false
]
% A0
\draw[blue,{Circle[fill=none]}-Stealth,thick] (axis cs:-.55, -26) node[n](a0) {A0} --
plot[smooth,mark=*] coordinates {
(axis cs:12,-25) (axis cs:19,-23) (axis cs:24,-19.5) (axis cs:28,-14) (axis cs:31,-13)
(axis cs:34,-7) (axis cs:36,-3) (axis cs:38,3) (axis cs:40,7) (axis cs:42,14) (axis cs:43,19)
(axis cs:44,25) (axis cs:45,33)
} -- (axis cs:45.5,39);
% A1
\draw[black,{Circle[fill=none]}-Stealth,thick] (axis cs:11.55, -6) node[n](a1) {A1} --
plot[smooth,mark=*] coordinates {
(axis cs:24, -5) (axis cs:31, -3.5) (axis cs:36, -3) (axis cs:40, -2) (axis cs:43, -1)
(axis cs:46, 1) (axis cs:48, 2) (axis cs:50, 4) (axis cs:52, 6) (axis cs:54, 9) (axis cs:55, 11)
} -- (axis cs:58, 18);
% A2
\draw[cyan,{Circle[fill=none]}-Stealth,thick] (axis cs:23.55, -2) node[n](a2) {A2} --
plot[smooth,mark=*] coordinates {
(axis cs:36, -1.4) (axis cs:43, -1) (axis cs:48, -1) (axis cs:52, -.8) (axis cs:55, -.6)
(axis cs:58, 0) (axis cs:60, 1) (axis cs:62, 2.5) (axis cs:64, 4.5) (axis cs:66, 6.5) (axis cs:68, 9)
} -- (axis cs:71, 14);
% A3
\draw[darkgray,{Circle[fill=none]}-Stealth,thick] (axis cs:35.55, -1.4) node[n](a3) {A3} --
plot[smooth,mark=*] coordinates {
(axis cs:48, -1) (axis cs:55, -.5) (axis cs:60, 1) (axis cs:64, 3) (axis cs:67, 5.2)
(axis cs:70, 8.6) (axis cs:72, 11)
} -- (axis cs:74.5, 17);
% A4
\draw[purple,{Circle[fill=none]}-Stealth,thick] (axis cs:47.55, 0) node[n](a4) {A4} --
plot[smooth,mark=*] coordinates {
(axis cs:60, .5) (axis cs:67, 5) (axis cs:72, 12) (axis cs:75, 18)
} -- (axis cs:78, 26);
% A5
% .. controls (axis cs:66,3.6) and (axis cs:67,4.2) ..
\draw[orange,{Circle[fill=none]}-Stealth,thick] (axis cs:59.55,1.2) node[n](a5) {A5}
to[out=10, in=210] (axis cs:72, 12) --
plot[smooth,mark=*] coordinates {
(axis cs:72, 12) (axis cs:79, 26) (axis cs:84, 39)
} -- (axis cs:85.5, 46);
% A6
% how to include a marking at (84, 38)? this breaks the path for some reason: (axis cs:84, 38) circle(2pt) --
% .. controls (axis cs:73,13) and (axis cs:77,20) ..
\draw[teal,{Circle[fill=none]}-Stealth,thick] (axis cs:71.6, 11.55) node[n](a6) {A6}
to[out=35, in=240] (axis cs:84, 39) to[out=60, in=250] (axis cs:87, 54);
% A7
% .. controls (axis cs:85,43) and (axis cs:86,45) ..
\draw[gray,{Circle[fill=none]}-Stealth,thick] (axis cs:83.75, 38) node[n](a7) {A7}
to[out=60, in=250] (axis cs:88, 58);
\node [pin=320:$A0^8-A1^4$] at (36,-3) {};
\node [pin=85:$A1^6-A2^3$] at (43, -1) {};
\node [pin=below right:$A2^4-A3^2$] at (48, -1) {};
\node [pin=below right:$A3^4-A4^2$] at (60, 1) {};
\node [pin=above left:$A4^4-A5^2$] at (72, 12) {};
\node [pin=below right:$A5^2-A6^1$] at (72, 12) {};
\node [pin=below right:$A6^2-A7^1$] at (84, 38) {};
\end{axis}
\end{tikzpicture}
\end{document}
Note that:
- I made ugly tweaks to the starting point to fake a centered anchor for the Circle arrows (the "fundamentals" which should indeed be positioned on the grid)
- I couldn't get the inline
to[]
andplot[smooth]
to result in a smooth curve, especially for the orange path - I don't know how to include a
*
mark shape manually withcircle
in the A6 path (see comment) without breaking the path (the arrows disappear or jump to funky places), and scaling the other markings means that I'm not sure about which diameter to hardcode
I wounder how one would overwrite or discard specific markings. Can this be done with forget plot
and postaction
?
edit:
per request of @marmot, plot data in files:
\begin{filecontents*}{A0.txt}
0 -26
12 -25
19 -23
24 -19.5
28 -14
31 -13
34 -7
36 -3
38 3
40 7
42 14
43 19
44 25
45 33
45 38
\end{filecontents*}
\begin{filecontents*}{A1.txt}
12 -6
24 -5
31 -3.5
36 -3
40 -2
43 -1
46 1
48 2
50 4
52 6
54 9
55 11
58 18
\end{filecontents*}
\begin{filecontents*}{A2.txt}
24 -2
36 -1.4
43 -1
48 -1
52 -.8
55 -.6
58 0
60 1
62 2.5
64 4.5
66 6.5
68 9
71 14
\end{filecontents*}
\begin{filecontents*}{A3.txt}
36 -1.4
48 -1
55 -.5
60 1
64 3
67 5.2
70 8.6
72 11
74 16
\end{filecontents*}
\begin{filecontents*}{A4.txt}
48 0
60 .5
67 5
72 12
75 18
78 26
\end{filecontents*}
\begin{filecontents*}{A5.txt}
60 1.2
72 12
72 12
79 26
84 39
86 44
\end{filecontents*}
\begin{filecontents*}{A6.txt}
72 11.55
84 39
87 54
\end{filecontents*}
\begin{filecontents*}{A7.txt}
84 38
88 58
\end{filecontents*}
(my data still needs to be processed, so these values are incomplete and illustrative only)
scatter
and@pre marker code
and/orvisualization depends on
.