Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Extracting data points from Plot[]

1,795 views
Skip to first unread message

Jon Guyer

unread,
Apr 14, 1995, 3:00:00 AM4/14/95
to
The Plot[] function does some intelligent picking of points, such that it
usually does a pretty good job of following twists and turns in a function
without plotting an excessive number of points. I'm not ecstatic about
using Mma for final output, though, so I like to export these curves to a
graphing program.

So far, I haven't been able to figure out any way to get at this
point-picking algorithm without using Plot[]. What I'm then left with is
doing:

In[1]:= Plot[{x,x^2},{x,0,2}];

In[2]:= InputForm[%1][[1]][[1]][[1]][[1]][[1]][[1]]

Out[2]= {{0., 0.}, {0.0833333, 0.0833333}, {0.166667, 0.166667},

> {0.25, 0.25}, {0.333333, 0.333333}, {0.416667, 0.416667}, {0.5, 0.5},

> {0.583333, 0.583333}, {0.666667, 0.666667}, {0.75, 0.75},

> {0.833333, 0.833333}, {0.916667, 0.916667}, {1., 1.},

> {1.08333, 1.08333}, {1.16667, 1.16667}, {1.25, 1.25},

> {1.33333, 1.33333}, {1.41667, 1.41667}, {1.5, 1.5}, {1.58333, 1.58333},

> {1.66667, 1.66667}, {1.75, 1.75}, {1.83333, 1.83333},

> {1.91667, 1.91667}, {2., 2.}}

In[3]:= InputForm[%1][[1]][[1]][[2]][[1]][[1]][[1]]

-6
Out[3]= {{0., 0.}, {0.00260417, 6.78168 10 }, {0.00520833, 0.0000271267},

> {0.0078125, 0.0000610352}, {0.0104167, 0.000108507},

> {0.0130208, 0.000169542}, {0.015625, 0.000244141},

> {0.0208333, 0.000434028}, {0.0260417, 0.000678168},

> {0.03125, 0.000976562}, {0.0416667, 0.00173611},

> {0.0520833, 0.00271267}, {0.0625, 0.00390625}, {0.0833333, 0.00694444},

> {0.104167, 0.0108507}, {0.125, 0.015625}, {0.166667, 0.0277778},

> {0.208333, 0.0434028}, {0.25, 0.0625}, {0.333333, 0.111111},

> {0.416667, 0.173611}, {0.5, 0.25}, {0.583333, 0.340278},

> {0.666667, 0.444444}, {0.75, 0.5625}, {0.833333, 0.694444},

> {0.916667, 0.840278}, {1., 1.}, {1.08333, 1.17361}, {1.16667, 1.36111},

> {1.25, 1.5625}, {1.33333, 1.77778}, {1.41667, 2.00694}, {1.5, 2.25},

> {1.58333, 2.50694}, {1.66667, 2.77778}, {1.75, 3.0625},

> {1.83333, 3.36111}, {1.91667, 3.67361}, {2., 4.}}

I can then export this to a useably formatted text file of data with an
arcane little macro I got from somebody.

What I'm wondering, though, is if there isn't something a bit more
straightforward than "InputForm[%1][[1]][[1]][[2]][[1]][[1]][[1]]" to get
at the data points? Is there a way to generate these data series the way
Plot[] does, without using Plot (which necessitates stripping off the
"Graphics" and "Line" structures which make up the bulk of the
sub-sub-sub-arrays that I'm referencing).

Am I being obtuse?

--
Jonathan E. Guyer j-g...@nwu.edu


Robert Villegas

unread,
Apr 15, 1995, 3:00:00 AM4/15/95
to
> The Plot[] function does some intelligent picking of points, such that
it
> usually does a pretty good job of following twists and turns in a
function
> without plotting an excessive number of points. I'm not ecstatic about
> using Mma for final output, though, so I like to export these curves to
a
> graphing program.

..

> What I'm wondering, though, is if there isn't something a bit more
> straightforward than "InputForm[%1][[1]][[1]][[2]][[1]][[1]][[1]]" to
get
> at the data points? Is there a way to generate these data series the
way
> Plot[] does, without using Plot (which necessitates stripping off the
> "Graphics" and "Line" structures which make up the bulk of the
> sub-sub-sub-arrays that I'm referencing).

Fortunately, there is no need to try to pin-point the Line structures
yourself.


One thing you can do is save the samples that Plot chooses by appending
each one to some list. If you're plotting f[x] on the interval [a, b]
you can do:

samples = {}

Plot[ (AppendTo[samples, {x, f[x]}]; f[x]), {x, a, b},
DisplayFunction->Identity ];

Now 'samples' will contain all the sample points that Plot generated
while working toward a Plot, and in the same order in which Plot
generated them. Since Plot uses an adaptive sampling routine that
sometimes goes back over an interval and samples it more finely,
the list of points in 'samples' may not always progress from left
to right. Don't forget to include "samples = {}" in each evaluation
you do, otherwise 'samples' will keep accumulating points from previous
Plot's.

(This method is given in the Course Notes _Numerical Computation in
Mathematica_ by Jerry Keiper and Dave Withoff, on page 1. It is
also given, in the context of a thorough description of Plot's adaptive
sampling, in Tom Wickham-Jones' book _Mathematica Graphics: Techniques
& Applications_, p. 583. The same method can be used for other iterative
commands like NIntegrate and FindRoot)


If f is expensive to compute, then you may not want to evaluate it
twice like I did above. You can use a pure function that will put
f[x] to use twice, once for the AppendTo that does the record-keeping,
and once as the return value of the whole function:

Plot[ (AppendTo[samples, {x, #}]; #)& [ f[x] ], {x, a, b},
DisplayFunction->Identity ];

This gives up some simplicity of appearance for efficiency when the latter
is more important.


This will give you the entire set of values that Plot _tried_
in the course of sampling, but sometimes Plot will throw out points,
say if the function doesn't evaluate to a real number at some of them.
For instance, Sqrt[x^2 - 4] isn't real on (-2, 2), so if you plot it
on [-3, 3], there will be two disconnected curves, hence two separate
Line objects. You can use Cases to find the Line objects and pull out
their points, without having to know exactly where they are in
the Graphics expression.


In[60]:= p = Plot[Sqrt[x^2 - 4], {x, -3, 3}]

[... error messages deleted ...]

Out[60]= -Graphics-

In[61]:= subcurves = Cases[First[p], Line[pts_List] :> pts, -1]

Out[61]=
{{{-3., 2.23607}, {-2.75, 1.88746}, {-2.5, 1.5}, {-2.375, 1.28087},

[ ...some lines deleted... ]

{2.25, 1.03078}, {2.5, 1.5}, {2.75, 1.88746}, {3., 2.23607}}}


If the function is well-behaved throughout the interval and there is just
one Line object, then First[subcurves] will give you a list of pairs.


Really, 'Cases' is a general command to extract all pieces of an
expression that have a certain structure or possess certain properties;
this is just one small application. See p. 221 of The Mathematica Book
for more on it.

Robby Villegas


Ian Collier

unread,
Apr 15, 1995, 3:00:00 AM4/15/95
to
In article <newsclip4413mkue3$r...@news0.cybernetics.net>, j-g...@nwu.edu
(Jon Guyer) wrote:

> What I'm wondering, though, is if there isn't something a bit more
> straightforward than "InputForm[%1][[1]][[1]][[2]][[1]][[1]][[1]]" to get
> at the data points? Is there a way to generate these data series the way
> Plot[] does, without using Plot (which necessitates stripping off the
> "Graphics" and "Line" structures which make up the bulk of the
> sub-sub-sub-arrays that I'm referencing).
>

> Am I being obtuse?
>
> --
> Jonathan E. Guyer j-g...@nwu.edu

Here is a method (taken from "The Mathematica Graphics
Guidebook"). (Note that I only use Short so that the resulting
list of numbers does not take up too much bandwidth.

In[1]:=
gr1 = Plot[ Sin[x], {x,0,2 Pi}]

Out[1]=
-Graphics-
In[5]:=
p1 = Nest[ First, gr1, 4]//Short
Out[5]//Short=
-19
{{0., 0.}, <<78>>, {6.28319, 1.0842 10 }}

"The Mathematica Graphics Guidebook" by Cameron Smith and
Nancy Blachman is published by Addison-Wesley.

I hope this helps.

--Ian

----------------------------------------------------------------
Ian Collier
Technical Sales Support
Wolfram Research, Inc.
----------------------------------------------------------------
Tel (217) 398-0700 Fax (217) 398-0747 ia...@wri.com
Wolfram Research Home Page http://www.wri.com/
----------------------------------------------------------------


Robert Villegas

unread,
Apr 17, 1995, 3:00:00 AM4/17/95
to
I just noticed something from Jon Guyer's post, which deserves a couple
comments. He was plotting several functions, not just one, and I had
the plotting of a single function in mind in my last post.

[his example]

> Plot[{x,x^2},{x,0,2}];


Something that might not be immediately obvious (I didn't realize it
until someone sent in a question a while back) is that if you give
Plot a list of functions, it samples them separately, in order. They
may behave very differently on the domain, and require completely
different distributions of sample points to be graphed smoothly.

A quick way to show this is to make two functions that store samples
in different lists when they are evaluated, and then look at the lists
afterwards. I set PlotPoints and PlotDivision low, so that not many
samples would be generated and the lists wouldn't take up much space
on the page.


In[1]:= f[x_] := (AppendTo[fList, x]; x)

In[2]:= g[x_] := (AppendTo[gList, x]; x^2)

In[3]:= fList = gList = {}

Out[3]= {}

In[4]:= Plot[{f[x], g[x]}, {x, -3, 3}, PlotPoints->5, PlotDivision->5]

Out[4]= -Graphics-

In[5]:= fList

Out[5]= {-3., -1.5, 0., 1.5, 3.}


(* x^2 is a more active function than x is, so it needs more sampling: *)

In[6]:= gList

Out[6]= {-3., -1.5, 0., -2.25, -0.75, -1.875, -1.125, -0.375, -0.9375,

> -0.5625, 1.5, 0.75, 0.375, -0.1875, 0.1875, 1.125, 0.5625, 3., 2.25,

> 1.875}


So this is one important thing to know if you're using the AppendTo trick
to store samples.


Also, this affects the way I used Cases to extract the Line objects
out of the Graphics expression. If you're plotting several functions,
not just one like I was assuming, you need to modify that method.
Here's an example of how to do it (the only difference is the use of
Map):

In[4]:= p = Plot[{x, x^2, x^3}, {x, 0, 1}]

Out[4]= -Graphics-

In[5]:= data = First[p];

In[6]:= {curve1, curve2, curve3} =
Map[Cases[#, Line[pts_] :> pts, -1]&, data];


The first part of the Graphics expression, which I called 'data',
still contains all the Line objects you want. The first element
of 'data' will contain the Line's (possibly just one) for the first
function, the second element of 'data' will contain the Line's for
the second function, and so on. This keeps the curves separate, and
knowing this, you can extract separate lists of points.


I'm sorry that my first post probably caused confusion for people trying
to use the Cases method directly on a plot of several functions.


Robby Villegas


Jarl R Sobel

unread,
Apr 19, 1995, 3:00:00 AM4/19/95
to
In article <3mkue3$r...@news0.cybernetics.net>, j-g...@nwu.edu (Jon Guyer)
wrote:

> The Plot[] function does some intelligent picking of points, such that it


> usually does a pretty good job of following twists and turns in a function
> without plotting an excessive number of points. I'm not ecstatic about
> using Mma for final output, though, so I like to export these curves to a
> graphing program.
>

> So far, I haven't been able to figure out any way to get at this
> point-picking algorithm without using Plot[]. What I'm then left with is
> doing:
>
> In[1]:= Plot[{x,x^2},{x,0,2}];
>
> In[2]:= InputForm[%1][[1]][[1]][[1]][[1]][[1]][[1]]
>
> Out[2]= {{0., 0.}, {0.0833333, 0.0833333}, {0.166667, 0.166667},
>

>etc etc

>
> I can then export this to a useably formatted text file of data with an
> arcane little macro I got from somebody.
>

> What I'm wondering, though, is if there isn't something a bit more
> straightforward than "InputForm[%1][[1]][[1]][[2]][[1]][[1]][[1]]" to get
> at the data points? Is there a way to generate these data series the way
> Plot[] does, without using Plot (which necessitates stripping off the
> "Graphics" and "Line" structures which make up the bulk of the
> sub-sub-sub-arrays that I'm referencing).
>
> Am I being obtuse?
>
> --
> Jonathan E. Guyer j-g...@nwu.edu

There is indeed another way to do this using Plot. The idea is from a back
issue of "The Mathematica Journal", I think it was in the "Tricks of the
Trade" column.

The method is based on the fact that the output of a CompoundExpression is
equal to the output of the last expression.

Thus

In[1]:=
x2={};

Plot[ x2=AppendTo[x2,x];x^2,
{x,0,2},
DisplayFunction->Identity];
x2
Out[3]=
{0., 0., 0.0833333, 0.0833333, 0.166667, 0.0416667,

0.0208333, 0.0104167, 0.00520833, 0.00260417, 0.015625,

0.0130208, 0.0078125, 0.03125, 0.0260417, 0.0625,

0.0520833, 0.125, 0.104167, 0.25, 0.208333, 0.333333,

0.416667, 0.5, 0.583333, 0.666667, 0.75, 0.833333,

0.916667, 1., 1.08333, 1.16667, 1.25, 1.33333, 1.41667,

1.5, 1.58333, 1.66667, 1.75, 1.83333, 1.91667, 2.}

will do the job for you.

Note that the order of the x-points is the order in which Mathematica tries
them. You might therfore want to sort them in ascending order before
plotting.

In[4]:=
Transpose[{Sort[x2],Sort[x2]^2}]

The method works only when you plot one curve at a time.

Jarl R Sobel

Jason Tigg SWAP GROUP ANALYST X2312 LDN

unread,
Apr 20, 1995, 3:00:00 AM4/20/95
to
J. Gruyer wrote

> The Plot[] function does some intelligent picking of points, such that it
> usually does a pretty good job of following twists and turns in a function
> without plotting an excessive number of points. I'm not ecstatic about
> using Mma for final output, though, so I like to export these curves to a
> graphing program.
>
> So far, I haven't been able to figure out any way to get at this
> point-picking algorithm without using Plot[]. What I'm then left with is
> doing:


Here's a trick which I picked up at a Mathematica conference.

In[1] := data = {};
f[x_] := x^2;
Plot[ AppendTo[data, {x,f[x]}]; f[x], {x,0,2},
DisplayFunction -> Identity];
data // Short

Out[4] = {{0,0},{0.0833333, 0.00694444}, <<37>>, {2,4}}

This method can be used to investigate where FindMinimum samples a function
in its search for a minimum.

Jason Tigg


Allan Hayes

unread,
Apr 20, 1995, 3:00:00 AM4/20/95
to
Jon Guyer asked if we can get the list of point coordinates used by
Plot without using Plot.

I don't know how; but we can avoid some overheads.

In[1]:=
pl = Plot[{x^2},{x,0,2},DisplayFunction -> Identity];

In[2]:=
coordinates = pl[[1,1,1,1,1]];

If more data is needed about the plot try the following -- I won't
show the long outputs.

In[3]:=
FullGraphics[pl][[1]];
and
In[4]:=
FullOptions[pl];

The latter can be limited to particular options:
In[5]:=
FullOptions[pl, PlotRange]
Out[5]=
{{-0.05, 2.05}, {-0.1, 4.1}}
In[6]:=
FullOptions[pl,{PlotRange, AspectRatio}]
Out[6]=
{{{-0.05, 2.05}, {-0.1, 4.1}}, 0.618034}

Allan Hayes
h...@haystack.demon.co.uk

Preston Nichols

unread,
Apr 20, 1995, 3:00:00 AM4/20/95
to
Jon Guyer wrote:

---------begin-excerpt----------------


The Plot[] function does some intelligent picking of points, such that it
usually does a pretty good job of following twists and turns in a
function
without plotting an excessive number of points. I'm not ecstatic about
using Mma for final output, though, so I like to export these curves to a
graphing program.

So far, I haven't been able to figure out any way to get at this
point-picking algorithm without using Plot[]. What I'm then left with is
doing:

In[1]:= Plot[{x,x^2},{x,0,2}];

In[2]:= InputForm[%1][[1]][[1]][[1]][[1]][[1]][[1]]

[Deletia]

What I'm wondering, though, is if there isn't something a bit more
straightforward than "InputForm[%1][[1]][[1]][[2]][[1]][[1]][[1]]" to get
at the data points?

------------end-excerpt----------------

****************************
Here's something that picks the Parts more elegantly, though it
still uses Plot:

PointsFromPlot::usage =
"PointsFromPlot[f, {x, xmin, xmax}] evaluates
Plot[f, {x, xmin, xmax}] but returns a list
of the points {x, f[x]} instead of a Graphics object.
PointsFromPlot[{f1, f2, ...}, {x, xmin, xmax}] computes
points for several functions fi. PointsFromPlot takes
the same options as Plot.";

PointsFromPlot[f_, {x_, xmin_,xmax_}, opts___] :=
Level[Cases[
Plot[f, {x, xmin, xmax}, opts,
DisplayFunction->Identity],
Line[_],Infinity], {-2,-2}]

The strategy is to use Cases to extract all the graphics primitives
(Line[_], in this case) at whatever level they may appear, and then
to use Level to get the points, since there is no longer any doubt
where they are (namely at level -2). The level is counted from the
bottom because Cases might return a List of Lines, especially if
more than one function is plotted. The definition is fixed so that
Mathematica will not render the plot unless you specify the option
DisplayFunction->$DisplayFunction, but in either case % will be the
list of points.

****************************
It's not hard to do a similar thing for Plot3D, though there is not
so much motivation for it. (Other than rendering options, Plot3D
doesn't do much that Table can't do equally well.) But you have to
change the SurfaceGraphics returned by Plot3D to Graphics3D as an
extra step:

PointsFromPlot3D::usage =
"PointsFromPlot3D[f, {x, xmin, xmax}, {y, ymin, ymax}]
evaluates Plot3D[f, {x, xmin, xmax}, {y, ymin, ymax}]
but returns a list of the points {x, y, f[x,y]} instead of a
SurfaceGraphics object. PointsFromPlot3D takes the same
options as Plot3D.";

PointsFromPlot3D[f_, {x_, xmin_,xmax_}, {y_, ymin_,ymax_}, opts___] :=
Level[Cases[
Graphics3D[Plot3D[f, {x, xmin, xmax}, {y, ymin,
ymax}, opts,
DisplayFunction->Identity]],
Polygon[_],Infinity], {-2,-2}]//Union

This is definitely not the best way to do the job, because in the
result of Graphics3D, every point appears at least twice, and most
points appear four times! (Postfixing Union cleans up the
duplicates.)

****************************
Here's one for ParametricPlot (almost identical to PointsFromPlot):

PointsFromParametricPlot::usage =
"PointsFromParametricPlot[{fx, fy}, {t, tmin, tmax}]
evaluates ParametricPlot[{fx, fy}, {t, tmin, tmax}]
but returns a list of the points {fx[t], fy[t]} instead of
a Graphics object. PointsFromParametricPlot[{{fx, fy},
{gx, gy}, ...}, {t, tmin, tmax}] computes points for
several parametric curves. PointsFromParametricPlot takes
the same options as ParametricPlot.";

PointsFromParametricPlot[f_, {x_, xmin_,xmax_}, opts___] :=
Level[Cases[
ParametricPlot[f, {x, xmin, xmax}, opts,
DisplayFunction->Identity],
Line[_],Infinity], {-2,-2}]

****************************
Unless someone advises me that this is a foolish procedure, I'll
build these (and a few more?) into a package PointsFromPlot.m and
send it to MathSource.

Preston Nichols
Department of Mathematics
Carnegie Mellon University

0 new messages