2
$\begingroup$

I have a list of associations :

testdata = {<|"label" -> "v1", "value1" -> -2, "value2" -> 6|>,
<|"label" -> "v2", "value1" -> 4, "value2" -> -4|>,
<|"label" -> "v3", "value1" -> 6, "value2" -> -5|>};

The first element of each association is the X axis label, each following key indicates the set of data it belongs to.

Using Vitali's answer here I have managed to get ListPlot to use each first element as X axis labels, but the code isn't the cleanest due to repeated usage of "testdata"

ListPlot[{testdata[[All, 2]], testdata[[All, 3]]}, Joined -> True, 
Ticks -> {Transpose[{Range@Length@testdata[[All, 1]], 
testdata[[All, 1]]}], Automatic}, 
PlotLegends -> DeleteDuplicates@Keys@testdata[[All, 2 ;; 3]]]

Using key names is a tiny bit clearer at the expense of verbosity:

ListPlot[{testdata[[All, "value1"]], testdata[[All, "value2"]]}, 
Joined -> True, 
Ticks -> {Transpose[{Range@Length@testdata[[All, "label"]], 
testdata[[All, "label"]]}], Automatic}, 
PlotLegends -> DeleteDuplicates@Keys@testdata[[All, 2 ;; 3]]]

I believe there must be a way to substitute in a pure function for this usage but am struggling to find it - any ideas?

Note that Im trying to avoid explicit use of Dataset due to its somewhat quirky behaviour in 10.0.x

BTW

Named keys in associations doesn't seem to support the ;; span specification syntax of Part - is this a bug?

testdata[[All,2;;3]] (* works *)
testdata[[All,"value1";;"value2"]] (* doesnt work *)  
$\endgroup$
3
  • $\begingroup$ You mean something like ListPlot[testdata[[All,#]]&/@{ "value1","value2"}, Joined -> True, Ticks -> {Transpose[{Range@Length@#, #}]&@testdata[[All, "label"]], Automatic}, PlotLegends -> DeleteDuplicates @Keys @ testdata[[All, 2 ;; 3]]]? $\endgroup$
    – kglr
    Commented Jan 2, 2015 at 19:37
  • $\begingroup$ @kguler Fairly close to what I was thinking, but was wondering if you can take the testdata completely outside of the main ListPlot expression completely, but somehow retain separate parts inside? Im probably trying to push things too far :) $\endgroup$ Commented Jan 2, 2015 at 21:18
  • $\begingroup$ maybe ListPlot[{#["value1"]&/@#,#["value2"]&/@#}, Joined -> True, Ticks -> {Transpose[{Range@Length@#, #}]&[#["label"]&/@#], Automatic}, PlotLegends -> DeleteDuplicates @Keys[#][[All,2;;]]]&@testdata? $\endgroup$
    – kglr
    Commented Jan 2, 2015 at 21:39

2 Answers 2

2
$\begingroup$

We can separate the data element selection from the ListPlot generation as follows:

testdata[[All, {"label", "value1", "value2"}]] //
ListPlot[
  Transpose @ Values @ #[[All, 2;;]]
, Joined -> True
, Ticks -> {MapIndexed[{#2[[1]], #}&, #[[All, 1]]], Automatic}
, PlotLegends -> DeleteDuplicates @ Keys @ #[[All, 2;;]]
]&

plot screenshot

Note the use of a list of keys in testdata[[All, {"label", "value1", "value2"}]]. It is not legal to use key names in spans since, in general, keys are not guaranteed to be in the same order within a list of associations.

The pure function is set up to assume that the first data element is the label and that the remaining data elements are to be plotted. This allows us to use the expression for other association lists where the keys are different, and appear in jumbled order:

testdata2 =
  { <|"x1" -> -10, "x2" -> 30, "title" -> "v1"|>
  , <|"x1" -> 20, "title" -> "v2", "x2" -> 22|>
  , <|"x2" -> 20, "x1" -> 30, "title" -> "v3"|>
  };

testdata2[[All, {"title", "x1", "x2"}]] //
ListPlot[
  Transpose @ Values @ #[[All, 2;;]]
, Joined -> True
, Ticks -> {MapIndexed[{#2[[1]], #}&, #[[All, 1]]], Automatic}
, PlotLegends -> DeleteDuplicates @ Keys @ #[[All, 2;;]]
]&

plot screenshot

It is inconvenient to copy-and-paste that long pure function expression each time, so we might consider packaging it up for reuse:

myListPlot[data_] :=
  ListPlot[
    Transpose @ Values @ data[[All, 2;;]]
  , Joined -> True
  , Ticks -> {MapIndexed[{#2[[1]], #}&, data[[All, 1]]], Automatic}
  , PlotLegends -> DeleteDuplicates @ Keys @ data[[All, 2;;]]
  ]

testdata[[All, {"label", "value1", "value2"}]] // myListPlot

plot screenshot

testdata2[[All, {"title", "x1", "x2"}]] // myListPlot

plot screenshot

Alternatively, we could have the caller explicitly specify the label and data key names:

myListPlot2[data_, labelKey_, {dataKeys__}] :=
  data[[All, {labelKey, dataKeys}]] // myListPlot

myListPlot2[testdata2, "title", {"x1", "x2"}]

plot screenshot

$\endgroup$
2
  • $\begingroup$ Lovely - exactly what I had in mind. Still digesting kgulers answer below before marking as answered. $\endgroup$ Commented Jan 3, 2015 at 12:39
  • $\begingroup$ Lovely answer: addresses all my issues (including the span question) and also contains a logical extrapolation of where I wanted to go. $\endgroup$ Commented Jan 5, 2015 at 14:57
1
$\begingroup$

You can also do:

ClearAll[listplotF];
listplotF[opts_:{Joined->True}]:= Module[{t, d = Join @@ Through @ 
            {Composition[Transpose, Values], Composition[DeleteDuplicates, Rest/@#&, Keys]}@#}, 
   ListPlot[{#2,#3}, Ticks->(t={Thread[{Range@Length@#,#}],Automatic}),
            FrameTicks->t,
            PlotLegends -> #4, Sequence@@opts]&@@d]&;

listplotF[]@testdata

enter image description here

listplotF[{Joined->True,PlotTheme->"Marketing"}]@testdata

enter image description here

$\endgroup$
1
  • $\begingroup$ Several new functions to me in that - thanks. $\endgroup$ Commented Jan 5, 2015 at 14:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.