| 1 | """ |
|---|
| 2 | List Plots |
|---|
| 3 | """ |
|---|
| 4 | |
|---|
| 5 | from sage.matrix.all import is_Matrix, matrix |
|---|
| 6 | from sage.rings.all import RDF |
|---|
| 7 | |
|---|
| 8 | def list_plot3d(v, interpolation_type='default', texture="automatic", point_list=None,**kwds): |
|---|
| 9 | """ |
|---|
| 10 | A 3-dimensional plot of a surface defined by the list $v$ of |
|---|
| 11 | points in 3-dimensional space. |
|---|
| 12 | |
|---|
| 13 | INPUT: |
|---|
| 14 | v -- something that defines a set of points in 3 space, |
|---|
| 15 | for example: |
|---|
| 16 | * a matrix |
|---|
| 17 | * a list of 3-tuples |
|---|
| 18 | * a list of lists (all of the same length) -- this |
|---|
| 19 | is treated the same as a matrix. |
|---|
| 20 | texture -- (default: "automatic"), solid light blue |
|---|
| 21 | |
|---|
| 22 | OPTIONAL KEYWORDS: |
|---|
| 23 | |
|---|
| 24 | interpolation_type - 'linear', 'nn' (nearest neighbor), 'spline' |
|---|
| 25 | |
|---|
| 26 | 'linear' will perform linear interpolation |
|---|
| 27 | |
|---|
| 28 | The option 'nn' will interpolate by averaging the value |
|---|
| 29 | of the nearest neighbors, this produces an interpolating function that is smoother than a linear interpolation, it |
|---|
| 30 | has one derivative everywhere except at the sample points. |
|---|
| 31 | |
|---|
| 32 | The option 'spline' interpolates using a bivariate B-spline. |
|---|
| 33 | |
|---|
| 34 | When v is a matrix the default is to use linear interpolation, |
|---|
| 35 | when v is a list of points the default is nearest neighbor. |
|---|
| 36 | |
|---|
| 37 | degree - an integer between 1 and 5, controls the degree of spline |
|---|
| 38 | used for spline interpolation. For data that is highly oscillatory |
|---|
| 39 | use higher values |
|---|
| 40 | |
|---|
| 41 | point_list - If point_list=True is passed, then if the array is a |
|---|
| 42 | list of lists of length three, it will be treated as an array of |
|---|
| 43 | points rather than a 3xn array. |
|---|
| 44 | |
|---|
| 45 | num_points - Number of points to sample interpolating function in each direction. By default |
|---|
| 46 | for an nxn array this is n. |
|---|
| 47 | |
|---|
| 48 | |
|---|
| 49 | **kwds -- all other arguments are passed to the surface function |
|---|
| 50 | |
|---|
| 51 | OUTPUT: |
|---|
| 52 | a 3d plot |
|---|
| 53 | |
|---|
| 54 | EXAMPLES: |
|---|
| 55 | We plot a matrix that illustrates summation modulo $n$. |
|---|
| 56 | sage: n = 5; list_plot3d(matrix(RDF,n,[(i+j)%n for i in [1..n] for j in [1..n]])) |
|---|
| 57 | |
|---|
| 58 | We plot a matrix of values of sin. |
|---|
| 59 | sage: pi = float(pi) |
|---|
| 60 | sage: m = matrix(RDF, 6, [sin(i^2 + j^2) for i in [0,pi/5,..,pi] for j in [0,pi/5,..,pi]]) |
|---|
| 61 | sage: list_plot3d(m, texture='yellow', frame_aspect_ratio=[1,1,1/3]) |
|---|
| 62 | |
|---|
| 63 | Though it doesn't change the shap of the graph, increasing num_points can increase the clarity of the |
|---|
| 64 | graph |
|---|
| 65 | sage: list_plot3d(m, texture='yellow', frame_aspect_ratio=[1,1,1/3],num_points=40) |
|---|
| 66 | |
|---|
| 67 | We can change the interpolation type |
|---|
| 68 | sage: list_plot3d(m, texture='yellow', interpolation_type='nn',frame_aspect_ratio=[1,1,1/3]) |
|---|
| 69 | |
|---|
| 70 | We can make this look better by increasing the number of samples |
|---|
| 71 | sage: list_plot3d(m, texture='yellow', interpolation_type='nn',frame_aspect_ratio=[1,1,1/3],num_points=40) |
|---|
| 72 | |
|---|
| 73 | Lets try a spline |
|---|
| 74 | sage: list_plot3d(m, texture='yellow', interpolation_type='spline',frame_aspect_ratio=[1,1,1/3]) |
|---|
| 75 | |
|---|
| 76 | That spline doesn't capture the oscillation very well, lets try a higher degree spline |
|---|
| 77 | sage: list_plot3d(m, texture='yellow', interpolation_type='spline', degree=5, frame_aspect_ratio=[1,1,1/3]) |
|---|
| 78 | |
|---|
| 79 | |
|---|
| 80 | We plot a list of lists: |
|---|
| 81 | sage: show(list_plot3d([[1, 1, 1, 1], [1, 2, 1, 2], [1, 1, 3, 1], [1, 2, 1, 4]])) |
|---|
| 82 | |
|---|
| 83 | We plot a list of points: |
|---|
| 84 | As a first example we can extract the (x,y,z) coordinates from the above example and |
|---|
| 85 | make a list plot out of it. By default we do linear interpolation. |
|---|
| 86 | |
|---|
| 87 | sage: l=[] |
|---|
| 88 | sage: for i in range(6): |
|---|
| 89 | ... for j in range(6): |
|---|
| 90 | ... l.append((float(i*pi/5),float(j*pi/5),m[i,j])) |
|---|
| 91 | sage: list_plot3d(l,texture='yellow') |
|---|
| 92 | |
|---|
| 93 | |
|---|
| 94 | Note that the points do not have to be regularly sampled. For example |
|---|
| 95 | |
|---|
| 96 | sage: l=[] |
|---|
| 97 | sage: T=Random() |
|---|
| 98 | sage: for i in range(-5,5) |
|---|
| 99 | ... for j in range(-5,5) |
|---|
| 100 | ... l.append((T.normalvariate(0,1),T.normalvariate(0,1),T.normalvariate(0,1))) |
|---|
| 101 | sage: list_plot3d(l,interpolation_type='nn',texture='yellow',num_points=100) |
|---|
| 102 | |
|---|
| 103 | |
|---|
| 104 | """ |
|---|
| 105 | import numpy |
|---|
| 106 | if texture == "automatic": |
|---|
| 107 | texture = "lightblue" |
|---|
| 108 | if is_Matrix(v): |
|---|
| 109 | if interpolation_type=='default' or interpolation_type=='linear' and not kwds.has_key('num_points'): |
|---|
| 110 | return list_plot3d_matrix(v, texture=texture, **kwds) |
|---|
| 111 | else: |
|---|
| 112 | l=[] |
|---|
| 113 | for i in xrange(v.nrows()): |
|---|
| 114 | for j in xrange(v.ncols()): |
|---|
| 115 | l.append((i,j,v[i,j])) |
|---|
| 116 | return list_plot3d_tuples(l,interpolation_type,texture,**kwds) |
|---|
| 117 | |
|---|
| 118 | if type(v)==numpy.ndarray: |
|---|
| 119 | return list_plot3d(matrix(v),interpolation_type,texture,**kwds) |
|---|
| 120 | |
|---|
| 121 | if isinstance(v, list): |
|---|
| 122 | if len(v) == 0: |
|---|
| 123 | # return empty 3d graphic |
|---|
| 124 | from base import Graphics3d |
|---|
| 125 | return Graphics3d() |
|---|
| 126 | elif isinstance(v[0],tuple) or point_list==True and len(v[0]) == 3: |
|---|
| 127 | return list_plot3d_tuples(v,interpolation_type,texture=texture, **kwds) |
|---|
| 128 | else: |
|---|
| 129 | return list_plot3d_array_of_arrays(v, interpolation_type,texture, **kwds) |
|---|
| 130 | raise TypeError, "v must be a matrix or list" |
|---|
| 131 | |
|---|
| 132 | def list_plot3d_matrix(m, texture, **kwds): |
|---|
| 133 | from parametric_surface import ParametricSurface |
|---|
| 134 | f = lambda i,j: (i,j,float(m[int(i),int(j)])) |
|---|
| 135 | G = ParametricSurface(f, (range(m.nrows()), range(m.ncols())), texture=texture, **kwds) |
|---|
| 136 | G._set_extra_kwds(kwds) |
|---|
| 137 | return G |
|---|
| 138 | |
|---|
| 139 | def list_plot3d_array_of_arrays(v, interpolation_type,texture, **kwds): |
|---|
| 140 | m = matrix(RDF, len(v), len(v[0]), v) |
|---|
| 141 | G = list_plot3d(m,interpolation_type,texture, **kwds) |
|---|
| 142 | G._set_extra_kwds(kwds) |
|---|
| 143 | return G |
|---|
| 144 | |
|---|
| 145 | def list_plot3d_tuples(v,interpolation_type, texture, **kwds): |
|---|
| 146 | import delaunay |
|---|
| 147 | import numpy |
|---|
| 148 | import scipy |
|---|
| 149 | from random import random |
|---|
| 150 | from scipy import interpolate |
|---|
| 151 | from scipy import stats |
|---|
| 152 | from plot3d import plot3d |
|---|
| 153 | |
|---|
| 154 | x=[float(p[0]) for p in v] |
|---|
| 155 | y=[float(p[1]) for p in v] |
|---|
| 156 | z=[float(p[2]) for p in v] |
|---|
| 157 | |
|---|
| 158 | corr_matrix=stats.corrcoef(x,y) |
|---|
| 159 | |
|---|
| 160 | if corr_matrix[0,1] > .9 or corr_matrix[0,1] < -.9: |
|---|
| 161 | # If the x,y coordinates lie in a one-dimensional subspace |
|---|
| 162 | # The scipy delauney code segfaults |
|---|
| 163 | # We compute the correlation of the x and y coordinates |
|---|
| 164 | # and add small random noise to avoid the problem |
|---|
| 165 | # if it looks like there is an issue |
|---|
| 166 | |
|---|
| 167 | ep=float(.000001) |
|---|
| 168 | x=[float(p[0])+random()*ep for p in v] |
|---|
| 169 | y=[float(p[1])+random()*ep for p in v] |
|---|
| 170 | |
|---|
| 171 | |
|---|
| 172 | xmin=float(min(x)) |
|---|
| 173 | xmax=float(max(x)) |
|---|
| 174 | ymin=float(min(y)) |
|---|
| 175 | ymax=float(max(y)) |
|---|
| 176 | |
|---|
| 177 | num_points= kwds['num_points'] if kwds.has_key('num_points') else int(4*numpy.sqrt(len(x))) |
|---|
| 178 | #arbitrary choice - assuming more or less a nxn grid of points |
|---|
| 179 | # x should have n^2 entries. We sample 4 times that many points. |
|---|
| 180 | |
|---|
| 181 | |
|---|
| 182 | |
|---|
| 183 | if interpolation_type == 'linear': |
|---|
| 184 | |
|---|
| 185 | T= delaunay.Triangulation(x,y) |
|---|
| 186 | f=T.linear_interpolator(z) |
|---|
| 187 | f.default_value=0.0 |
|---|
| 188 | j=numpy.complex(0,1) |
|---|
| 189 | vals=f[ymin:ymax:j*num_points,xmin:xmax:j*num_points] |
|---|
| 190 | from parametric_surface import ParametricSurface |
|---|
| 191 | |
|---|
| 192 | def g(x,y): |
|---|
| 193 | i=round( (x-xmin)/(xmax-xmin)*(num_points-1) ) |
|---|
| 194 | j=round( (y-ymin)/(ymax-ymin)*(num_points-1) ) |
|---|
| 195 | z=vals[int(j),int(i)] |
|---|
| 196 | return (x,y,z) |
|---|
| 197 | |
|---|
| 198 | |
|---|
| 199 | G = ParametricSurface(g, (list(numpy.r_[xmin:xmax:num_points*j]), list(numpy.r_[ymin:ymax:num_points*j])), texture=texture, **kwds) |
|---|
| 200 | G._set_extra_kwds(kwds) |
|---|
| 201 | return G |
|---|
| 202 | |
|---|
| 203 | |
|---|
| 204 | if interpolation_type == 'nn' or interpolation_type =='default': |
|---|
| 205 | |
|---|
| 206 | T=delaunay.Triangulation(x,y) |
|---|
| 207 | f=T.nn_interpolator(z) |
|---|
| 208 | f.default_value=0.0 |
|---|
| 209 | j=numpy.complex(0,1) |
|---|
| 210 | vals=f[ymin:ymax:j*num_points,xmin:xmax:j*num_points] |
|---|
| 211 | from parametric_surface import ParametricSurface |
|---|
| 212 | def g(x,y): |
|---|
| 213 | i=round( (x-xmin)/(xmax-xmin)*(num_points-1) ) |
|---|
| 214 | j=round( (y-ymin)/(ymax-ymin)*(num_points-1) ) |
|---|
| 215 | z=vals[int(j),int(i)] |
|---|
| 216 | return (x,y,z) |
|---|
| 217 | |
|---|
| 218 | G = ParametricSurface(g, (list(numpy.r_[xmin:xmax:num_points*j]), list(numpy.r_[ymin:ymax:num_points*j])), texture=texture, **kwds) |
|---|
| 219 | G._set_extra_kwds(kwds) |
|---|
| 220 | return G |
|---|
| 221 | |
|---|
| 222 | if interpolation_type =='spline': |
|---|
| 223 | from plot3d import plot3d |
|---|
| 224 | kx=kwds['kx'] if kwds.has_key('kx') else 3 |
|---|
| 225 | ky=kwds['ky'] if kwds.has_key('ky') else 3 |
|---|
| 226 | if kwds.has_key('degree'): |
|---|
| 227 | kx=kwds['degree'] |
|---|
| 228 | ky=kwds['degree'] |
|---|
| 229 | |
|---|
| 230 | s=kwds['smoothing'] if kwds.has_key('smoothing') else len(x)-numpy.sqrt(2*len(x)) |
|---|
| 231 | s=interpolate.bisplrep(x,y,z,[int(1)]*len(x),xmin,xmax,ymin,ymax,kx=kx,ky=ky,s=s) |
|---|
| 232 | f=lambda x,y: interpolate.bisplev(x,y,s) |
|---|
| 233 | return plot3d(f,(xmin,xmax),(ymin,ymax),texture=texture,plot_points=[num_points,num_points],**kwds) |
|---|