I have a data set that I want to plot as a quiver plot. The set contains parameters of stars represented as ellipses (center, long axis length, short axis length, bearing (angle) of the long axis) that I want to draw as a vector field with quiver plot. The data (axis lengths and bearings) is noisy and I need to smooth it. While smoothing works fine for axis length I'm stuck with angle smoothing. The problem here is that any ellipse is visually (and for my purposes too) equivalent to an ellipse rotated by 180 degrees and ellipses' angles are distributed in the range from 0 to 180. Smoothing for the most part of this range works fine except for corner cases where angles are close to 0 and close to 180. While two ellipses with angles close to 0 and close to 180 looks visually similar, their "average" has an angle of 90 degrees which is clearly wrong. See an example of unsmoothed data (white stars and green arrows representing these stars) where the smoothing breaks. To be clear I don't need the direction of the arrow, and in my final plot I'm hiding arrow heads. I need only the angle/bearing of it and the average of two angles of 179.9 and 0.1 needs to be either 180 or 0 but not 90.
Any idea how to approach the problem?
Two options:
First, you can convert your angle to an int and expand it to the full range of that int and take advantage of overflow. For example:
np.astype((ang-180.)/360.*65536, 'uint16')
This may work if your smoothing function is simple and doesn't have comparison operators in it and is in my opinion a nasty hack.
Second, rotate your angle by some arbitrary amount, and do the smoothing multiple times, picking the best answer.
I would smooth the original, then rotate by 120, smooth that, rotate by 240, smooth that. Then un-rotate and then keep only the angles that agree. Of course this comes with a 3x+ performance hit.
Edit:
Third option: Smooth the vector components.
Related
I'm having trouble using the scipy interpolation methods to generate a nice smooth curve from the data points given. I've tried using the standard 1D interpolation, the Rbf interpolation with all options (cubic, gaussian, multiquadric etc.)
in the image provided, the blue line is the original data, and I'm looking to first smooth the sharp edges, and then have dynamically editable points from which to recalculate the curve. Each time a single point is edited it should auto calculate a new spline of some sort to smoothly transition between each point.
It kind of works when the points are within a particular range of each other as below.
But if the points end up too far apart, or too close together, I end up with issues like the following.
Key points are:
The curve MUST be flat between the first two points
The curve must NOT go below point 1 or 2 (i.e. derivative can't be negative)
~15 points (not shown) between points 2 and 3 are also editable and the line between is not necessarily linear. Full control over each of these points is a must, as is the curve going through each of them.
I'm happy to break it down into smaller curves that i then join/convolve, but just need to ensure a >0 gradient.
sample data:
x=[0, 37, 50, 105, 115,120]
y=[0.00965, 0.00965, 0.047850827205882, 0.35600416666667, 0.38074375, 0.38074375]
As an example, try moving point 2 (x=37) to an extreme value, say 10 (keep y the same). Just ensure that all points from x=0 to x=10 (or any other variation) have identical y values of 0.00965.
any assistance is greatly appreciated.
UPDATE
Attempted pchip method suggested in comments with the results below:
pchip method, better and worse...
Solved!
While I'm not sure that this is exactly true, it is as if the spline tools for creating Bezier curves treat the control points as points the calculated curve must go through - which is not true in my case. I couldn't figure out how to turn this feature off, so I found the cubic formula for a Bezier curve (cubic is what I need) and calculated my own points. I only then had to do a little adjustment to make the points fit the required integer x values - in my case, near enough is good enough. I would otherwise have needed to interpolate linearly between two points either side of the desired x value and determine the exact value.
For those interested, cubic needs 4 points - start, end, and 2 control points. The rule is:
B(t) = (1-t)^3 P0 + 3(1-t)^2 tP1 + 3(1-t)t^2 P2 + t^3 P3
Calculate for x and y separately, using a list of values for t. If you need to gradient match, just make sure that the control points for P1 and P2 are only moved along the same gradient as the preceding/proceeding sections.
Perfect result
I have the coordinates of 6 points in an image
(170.01954650878906, 216.98866271972656)
(201.3812255859375, 109.42137145996094)
(115.70114135742188, 210.4272918701172)
(45.42426300048828, 97.89037322998047)
(167.0367889404297, 208.9329833984375)
(70.13690185546875, 140.90538024902344)
I have a point as center [89.2458, 121.0896]. I am trying to re-calculate the position of points in python using 4 rotation degree (from 0,90,-90,180) and 6 scaling factor (0.5,0.75,1,1.10,1.25,1.35,1.5).
My question is how can I rotate and scale the abovementioned points relative to the center point and get the new coordinates of those 6 points?
Your help is really appreciated.
Mathematics
A mathematical approach would be to represent this data as vectors from the center to the image-points, translate these vectors to the origin, apply the transformation and relocate them around the center point. Let's look at how this works in detail.
Representation as vectors
We can show these vectors in a grid, this will produce following image
This image provides a nice way to look at these points, so we can see our actions happening in a visual way. The center point is marked with a dot at the beginning of all the arrows, and the end of each arrow is the location of one of the points supplied in the question.
A vector can be seen as a list of the values of the coordinates of the point so
my_vector = [point[0], point[1]]
could be a representation for a vector in python, it just holds the coordinates of a point, so the format in the question could be used as is! Notice that I will use the position 0 for the x-coordinate and 1 for the y-coordinate throughout my answer.
I have only added this representation as a visual aid, we can look at any set of two points as being a vector, no calculation is needed, this is only a different way of looking at those points.
Translation to origin
The first calculations happen here. We need to translate all these vectors to the origin. We can very easily do this by subtracting the location of the center point from all the other points, for example (can be done in a simple loop):
point_origin_x = point[0] - center_point[0] # Xvalue point - Xvalue center
point_origin_y = point[1] - center_point[1] # Yvalue point - Yvalue center
The resulting points can now be rotated around the origin and scaled with respect to the origin. The new points (as vectors) look like this:
In this image, I deliberately left the scale untouched, so that it is clear that these are exactly the same vectors (arrows), in size and orientation, only shifted to be around (0, 0).
Why the origin
So why translate these points to the origin? Well, rotations and scaling actions are easy to do (mathematically) around the origin and not as easy around other points.
Also, from now on, I will only include the 1st, 2nd and 4th point in these images to save some space.
Scaling around the origin
A scaling operation is very easy around the origin. Just multiply the coordinates of the point with the factor of the scaling:
scaled_point_x = point[0] * scaling_factor
scaled_point_y = point[1] * scaling_factor
In a visual way, that looks like this (scaling all by 1.5):
Where the blue arrows are the original vectors and the red ones are the scaled vectors.
Rotating
Now for rotating. This is a little bit harder, because a rotation is most generally described by a matrix multiplication with this vector.
The matrix to multiply with is the following
(from wikipedia: Rotation Matrix)
So if V is the vector than we need to perform V_r = R(t) * V to get the rotated vector V_r. This rotation will always be counterclockwise! In order to rotate clockwise, we simply need to use R(-t).
Because only multiples of 90° are needed in the question, the matrix becomes a almost trivial. For a rotation of 90° counterclockwise, the matrix is:
Which is basically in code:
rotated_point_x = -point[1] # new x is negative of old y
rotated_point_y = point[0] # new y is old x
Again, this can be nicely shown in a visual way:
Where I have matched the colors of the vectors.
A rotation 90° clockwise will than be
rotated_counter_point_x = point[1] # x is old y
rotated_counter_point_y = -point[0] # y is negative of old x
A rotation of 180° will just be taking the negative coordinates or, you could just scale by a factor of -1, which is essentially the same.
As last point of these operations, might I add that you can scale and/or rotated as much as you want in a sequence to get the desired result.
Translating back to the center point
After the scaling actions and/or rotations the only thing left is te retranslate the vectors to the center point.
retranslated_point_x = new_point[0] + center_point_x
retranslated_point_y = new_point[1] + center_point_y
And all is done.
Just a recap
So to recap this long post:
Subtract the coordinates of the center point from the coordinates of the image-point
Scale by a factor with a simply multiplication of the coordinates
Use the idea of the matrix multiplication to think about the rotation (you can easily find these things on Google or Wikipedia).
Add the coordinates of the center point to the new coordinates of the image-point
I realize now that I could have just given this recap, but now there is at least some visual aid and a slight mathematical background in this post, which is also nice. I really believe that such problems should be looked at from a mathematical angle, the mathematical description can help a lot.
this is the graph in question and the dots should appear in the bottom plane, not "above" the plane like i manged to.
bx.scatter(xs,ys,zs, zdir=zs,c=plt.cm.jet(np.linspace(0,1,N))) # scatter points
for i in range(N-1):
bx.plot(xs[i:i+2], ys[i:i+2], zs[i:i+2], color=plt.cm.jet(i/N), alpha=0.5)
#plots the lines between points
bx.scatter(xs,ys,zs=732371.0,zdir="z",c=plt.cm.jet(np.linspace(0,1,N)),depthshade=True)
bx.set_zlim3d(732371.0,) #limit is there so that we can project the points onto the xy-plane
as youll notice the points are drawn above the xy-grid and I had to set a lower limit for the z-axis so that the first projected point will not interfere with the first scatter point
I would prefer the points be in 2d and less hacky since I got 50 other graphs to do like this and fine tune each one would be cumbersome.
Got some simpler method you want to share?
There are many options, and ultimately, it depends on the range of your data in the other plots.
1) Offset the projection point by a fixed amount
You could calculate the minimum Z value, and plot your projection a fixed offset from that minimum value.
zs=min(zs)-offset
2) offset the projection by a relative amount that depends on the range of your data.
You could take into account the range of your data (i.e. the distance from min to max Z) and calculate an offset proportional to that (e.g. 10-15%).
zs=min(zs)-0.15*(max(zs)-min(zs))
I have an issue that I can't seem to solve. I have already acquired data from another source and created 2 polynomials that are identical in shape but not in orientation, that is one is rotated x degrees compared to the other, and if you rotate the graph x degrees back they will match.
I have already taken the derivative of both of the graphs at a certain point.
I would like to graph these slopes onto a unit circle on a polar graph, and somehow find the angle difference between these two line segments of slope i and j that extend from the origin.
I'm fairly new to python so I so not know how to begin plotting these in polar or finding a way to determine the angle difference. I know that by hand, you can take the inverse tangent but that will only give you a range from +90 to -90. I would like my number to fall in the range from 0 to 360 for rotation.
Any help is appreciated. If this isn't enough info or if it isn't clear enough I can provide more.
I have 3d data produced from mesh points. The structure that was meshed is complex enough that interpolation using griddata is lacking. Specifically, there are regions without data points which are being given values by griddata that are not the fill_value. I need these hollow regions to have the value of 0.0, which I set fill_value to.
A simplified version of this is illustrated below:
The area occupied by the cylinder has no data points but the rest of the cube volume does. There will be data points from interpolation inside the cylinder but I need them to be zero.
Below is a slice parallel to the xy plane of the actual interpolated data with a black oval that approximates the edge 'cylinder'. The red an blue 'bleed' in to the void after interpolation. The fill value of 0.0 can be seen in the upper left corner:
Any ideas on how I can achieve the goal of setting those values to 0.0? Note that the 'cylinder' is not of constant shape.
I thought about going z layer by z layer and finding a polygon that gives the cylinder shape and then setting points inside the polygon to zero.
I also thought about partitioning the volume so a portion of the cylinder ends up in corners of the partion (for each z layer) and hoping that the interpolator would not try to extrapolate into the void region.
The first option seems better, but I would like to know if Python provides some sort of functionality which would work better.
EDIT: Here are some actual points from the data set:
The z scale is much smaller than x or y. You can see that the regions I'm interested in are pretty well defined. But, again, how do I identify them for the purposes of setting grid points to 0.0?