matplotlib in python - how to extracting data from contour lines - python

i want to get data from a contour of different iso-line. the question matplotlib - extracting data from contour lines gives an example
import matplotlib.pyplot as plt
x = [1,2,3,4]
y = [1,2,3,4]
m = [[15,14,13,12],[14,12,10,8],[13,10,7,4],[12,8,4,0]]
cs = plt.contour(x,y,m, [9.5])
plt.show()
coord = cs.collections[0].get_paths()
it get the coordinates of line with value euqal to 9.5.
now, i need to get the coordinate of multi-isoline from one contour, so i need to change the value to represent different line, but when i use loop, it means python needs to construct the contour at each loop, how can i construct the contour once and then change the value to represent different line?

You can plot several contours at once with plt.contour by giving a list of the values you wish to contour. Then, you can access them all from the returned ContourSet using cs.allsegs or by using get_paths on each item in the cs.collections list.
For example:
import matplotlib.pyplot as plt
x = [1,2,3,4]
y = [1,2,3,4]
m = [[15,14,13,12],[14,12,10,8],[13,10,7,4],[12,8,4,0]]
cs = plt.contour(x,y,m, [9.5, 10.5, 11.5])
plt.show()
# Option 1: use allsegs
all_coords = cs.allsegs
print(all_coords)
# Option 2: use cs.collections[X].get_paths()
coords1 = cs.collections[0].get_paths()
coords2 = cs.collections[1].get_paths()
coords3 = cs.collections[2].get_paths()
print(coords1)
print(coords2)
print(coords3)
Where the printed coords are then:
Option 1 (allsegs):
[[array([[4. , 1.625 ],
[3.25 , 2. ],
[3. , 2.16666667],
[2.16666667, 3. ],
[2. , 3.25 ],
[1.625 , 4. ]])],
[array([[4. , 1.375 ],
[3. , 1.83333333],
[2.75 , 2. ],
[2. , 2.75 ],
[1.83333333, 3. ],
[1.375 , 4. ]])],
[array([[4. , 1.125],
[3. , 1.5 ],
[2.25 , 2. ],
[2. , 2.25 ],
[1.5 , 3. ],
[1.125, 4. ]])]]
Option 2 (get_paths()):
[Path(array([[4. , 1.625 ],
[3.25 , 2. ],
[3. , 2.16666667],
[2.16666667, 3. ],
[2. , 3.25 ],
[1.625 , 4. ]]), array([1, 2, 2, 2, 2, 2], dtype=uint8))]
[Path(array([[4. , 1.375 ],
[3. , 1.83333333],
[2.75 , 2. ],
[2. , 2.75 ],
[1.83333333, 3. ],
[1.375 , 4. ]]), array([1, 2, 2, 2, 2, 2], dtype=uint8))]
[Path(array([[4. , 1.125],
[3. , 1.5 ],
[2.25 , 2. ],
[2. , 2.25 ],
[1.5 , 3. ],
[1.125, 4. ]]), array([1, 2, 2, 2, 2, 2], dtype=uint8))]

Related

How do I convert numpy mgrid function as a function?

Here is the way how numpy.mgrid is used.
grid = np.mgrid[x1:y1:100j , x2:y2:100j, ..., xn:yn:100j]
However, I find this structure very irritating. Therefore, I would like to create function f which works as follows:
f([(x1,y1,100),...,(xn,yn,100)]) = np.mgrid[x1:y1:100j , x2:y2:100j, ..., xn:yn:100j]
How can I create f?
(Here is the source code for np.mgrid)
Just loop over each item passed to f and make a slice out of it with slice, and to get 100j from 100, multiply 100 by 1j:
def f(items):
slices = [slice(i[0], i[1], 1j * i[2]) for i in items]
return np.mgrid[slices]
Output:
>>> np.all( f([(1,2,5), (2,3,5)]) == np.mgrid[1:2:5j, 2:3:5j] )
True
You could make calling the function even simpler by using *items instead of items:
def f(*items):
slices = [slice(i[0], i[1], 1j * i[2]) for i in items]
return np.mgrid[slices]
Output:
>>> np.all( f([1,2,5], [2,3,5]) == np.mgrid[1:2:5j, 2:3:5j] )
True
mgrid is an instance of a cute class that lets us use indexing notation. Under the covers it uses np.linspace (or np.arange) to generate the ranges.
In [29]: x1,y1 = 0,1; x2,y2 = 1,3
In [30]: np.mgrid[x1:y1:3j, x2:y2:4j]
Out[30]:
array([[[0. , 0. , 0. , 0. ],
[0.5 , 0.5 , 0.5 , 0.5 ],
[1. , 1. , 1. , 1. ]],
[[1. , 1.66666667, 2.33333333, 3. ],
[1. , 1.66666667, 2.33333333, 3. ],
[1. , 1.66666667, 2.33333333, 3. ]]])
meshgrid is the function equivalent. I suspect it was the original function, and mgrid (and ogrid) was secondary version:
In [31]: np.meshgrid(np.linspace(x1,y1,3), np.linspace(x2,y2,4), indexing='ij')
Out[31]:
[array([[0. , 0. , 0. , 0. ],
[0.5, 0.5, 0.5, 0.5],
[1. , 1. , 1. , 1. ]]),
array([[1. , 1.66666667, 2.33333333, 3. ],
[1. , 1.66666667, 2.33333333, 3. ],
[1. , 1.66666667, 2.33333333, 3. ]])]
mgrid creates a n-d array; meshgrid returns a list of arrays. Otherwise they are equivalent. np.array(Out[31]) creates the array.
sparse versions
ogrid produces a "sparse" pair of arrays that, with broadcasting, functions the same way:
In [37]: np.ogrid[x1:y1:3j, x2:y2:4j]
Out[37]:
[array([[0. ],
[0.5],
[1. ]]),
array([[1. , 1.66666667, 2.33333333, 3. ]])]
meshgrid has an equivalent sparse mode:
In [38]: np.meshgrid(np.linspace(x1,y1,3), np.linspace(x2,y2,4), indexing='ij',
...: sparse=True)
Out[38]:
[array([[0. ],
[0.5],
[1. ]]),
array([[1. , 1.66666667, 2.33333333, 3. ]])]
We can create the same pair of arrays with:
In [39]: np.ix_(np.linspace(x1,y1,3), np.linspace(x2,y2,4))
Out[39]:
(array([[0. ],
[0.5],
[1. ]]),
array([[1. , 1.66666667, 2.33333333, 3. ]]))
or even:
In [40]: (np.linspace(x1,y1,3)[:,None], np.linspace(x2,y2,4)[None,:])
Out[40]:
(array([[0. ],
[0.5],
[1. ]]),
array([[1. , 1.66666667, 2.33333333, 3. ]]))

How to put multiple conditions (one or and two and) in np.where function

I have a numpy array of several rows and three columns. I want to do a np.where function to find two rows among all the existing rows. The first row I want is the row that has the least values of the second column and also its third column is the minimum one among others. The second row is among the rows that have the highest values in their second column. And my target has the least value of in its third column among these rows. It is my data set:
arr=np.array([[6. , 1. , 1.2],
[5.5, 3. , 1.5],
[5. , 1. , 2. ],
[5. , 3. , 2. ],
[5. , 6. , 2. ],
[4. , 1. , 3. ],
[4. , 3. , 3. ],
[4. , 6. , 3. ],
[3. , 1. , 4. ],
[3. , 3. , 4. ],
[3. , 6. , 4. ]])
Then I tried using two & and one |:
arr[np.where(((arr[:,1]==min(arr[:,1])) & (arr[:,-1]==min(arr[:,-1]))) |
((arr[:,1]==max(arr[:,1])) & (arr[:,-1]==min(arr[:,-1]))))]
To get the
np.array([[6. , 1. , 1.2], [5. , 6. , 2. ]]
But it is only giving me:
np.array([[6. , 1. , 1.2]]
I do appreciate any help to solve my problem.
This should work as you expected
arr1 = arr[arr[:,1]==min(arr[:,1])]
arr1 = arr1[(arr1[:,-1]==min(arr1[:,-1]))]
arr2 = arr[(arr[:,1]==max(arr[:,1]))]
arr2 = arr2[(arr2[:,-1]==min(arr2[:,-1]))]
np.concatenate([arr1, arr2])
a1 = arr[:, 1]
a2 = arr[:, 2]
a1mn = a1.min()
a1mx = a1.max()
ia1mn = (a1 == a1mn)
ia1mx = (a1 == a1mx)
out = arr[(ia1mn & (a2 == a2[ia1mn].min())) | (ia1mx & (a2 == a2[ia1mx].min()))]

How to scale each column of a matrix

This is how I scale a single vector:
vector = np.array([-4, -3, -2, -1, 0])
# pass the vector, current range of values, the desired range, and it returns the scaled vector
scaledVector = np.interp(vector, (vector.min(), vector.max()), (-1, +1)) # results in [-1. -0.5 0. 0.5 1. ]
How can I apply the above approach to each column of a given matrix?
matrix = np.array(
[[-4, -4, 0, 0, 0],
[-3, -3, 1, -15, 0],
[-2, -2, 8, -1, 0],
[-1, -1, 11, 12, 0],
[0, 0, 50, 69, 80]])
scaledMatrix = [insert code that scales each column of the matrix]
Note that the first two columns of the scaledMatrix should be equal to the scaledVector from the first example. For the matrix above, the correctly computed scaledMatrix is:
[[-1. -1. -1. -0.64285714 -1. ]
[-0.5 -0.5 -0.96 -1. -1. ]
[ 0. 0. -0.68 -0.66666667 -1. ]
[ 0.5 0.5 -0.56 -0.35714286 -1. ]
[ 1. 1. 1. 1. 1. ]]
My current approach (wrong):
np.interp(matrix, (np.min(matrix), np.max(matrix)), (-1, +1))
If you want to do it by hand and understand what's going on:
First substract columnwise mins to make each columns have min 0.
Then divide by columnwise amplitude (max - min) to make each column have max 1.
Now each column is between 0 and 1. If you want it to be between -1 and 1, multiply by 2, and substract 1:
In [3]: mins = np.min(matrix, axis=0)
In [4]: maxs = np.max(matrix, axis=0)
In [5]: (matrix - mins[None, :]) / (maxs[None, :] - mins[None, :])
Out[5]:
array([[ 0. , 0. , 0. , 0.17857143, 0. ],
[ 0.25 , 0.25 , 0.02 , 0. , 0. ],
[ 0.5 , 0.5 , 0.16 , 0.16666667, 0. ],
[ 0.75 , 0.75 , 0.22 , 0.32142857, 0. ],
[ 1. , 1. , 1. , 1. , 1. ]])
In [6]: 2 * _ - 1
Out[6]:
array([[-1. , -1. , -1. , -0.64285714, -1. ],
[-0.5 , -0.5 , -0.96 , -1. , -1. ],
[ 0. , 0. , -0.68 , -0.66666667, -1. ],
[ 0.5 , 0.5 , -0.56 , -0.35714286, -1. ],
[ 1. , 1. , 1. , 1. , 1. ]])
I use [None, :] for numpy to understand that I'm talking about "row vectors", not column ones.
Otherwise, use the wonderful sklearn package, whose preprocessing module has lots of useful transformers:
In [13]: from sklearn.preprocessing import MinMaxScaler
In [14]: scaler = MinMaxScaler(feature_range=(-1, 1))
In [15]: scaler.fit(matrix)
Out[15]: MinMaxScaler(copy=True, feature_range=(-1, 1))
In [16]: scaler.transform(matrix)
Out[16]:
array([[-1. , -1. , -1. , -0.64285714, -1. ],
[-0.5 , -0.5 , -0.96 , -1. , -1. ],
[ 0. , 0. , -0.68 , -0.66666667, -1. ],
[ 0.5 , 0.5 , -0.56 , -0.35714286, -1. ],
[ 1. , 1. , 1. , 1. , 1. ]])

Explanation on Numpy Broadcasting Answer

I recently posted a question here which was answered exactly as I asked. However, I think I overestimated my ability to manipulate the answer further. I read the broadcasting doc, and followed a few links that led me way back to 2002 about numpy broadcasting.
I've used the second method of array creation using broadcasting:
N = 10
out = np.zeros((N**3,4),dtype=int)
out[:,:3] = (np.arange(N**3)[:,None]/[N**2,N,1])%N
which outputs:
[[0,0,0,0]
[0,0,1,0]
...
[0,1,0,0]
[0,1,1,0]
...
[9,9,8,0]
[9,9,9,0]]
but I do not understand via the docs how to manipulate that. I would ideally like to be able to set the increments in which each individual column changes.
ex. Column A changes by 0.5 up to 2, column B changes by 0.2 up to 1, and column C changes by 1 up to 10.
[[0,0,0,0]
[0,0,1,0]
...
[0,0,9,0]
[0,0.2,0,0]
...
[0,0.8,9,0]
[0.5,0,0,0]
...
[1.5,0.8,9,0]]
Thanks for any help.
You can adjust your current code just a little bit to make it work.
>>> out = np.zeros((4*5*10,4))
>>> out[:,:3] = (np.arange(4*5*10)[:,None]//(5*10, 10, 1)*(0.5, 0.2, 1)%(2, 1, 10))
>>> out
array([[ 0. , 0. , 0. , 0. ],
[ 0. , 0. , 1. , 0. ],
[ 0. , 0. , 2. , 0. ],
...
[ 0. , 0. , 8. , 0. ],
[ 0. , 0. , 9. , 0. ],
[ 0. , 0.2, 0. , 0. ],
...
[ 0. , 0.8, 9. , 0. ],
[ 0.5, 0. , 0. , 0. ],
...
[ 1.5, 0.8, 9. , 0. ]])
The changes are:
No int dtype on the array, since we need it to hold floats in some columns. You could specify a float dtype if you want (or even something more complicated that only allows floats in the first two columns).
Rather than N**3 total values, figure out the number of distinct values for each column, and multiply them together to get our total size. This is used for both zeros and arange.
Use the floor division // operator in the first broadcast operation because we want integers at this point, but later we'll want floats.
The values to divide by are again based on the number of values for the later columns (e.g. for A,B,C numbers of values, divide by B*C, C, 1).
Add a new broadcast operation to multiply by various scale factors (how much each value increases at once).
Change the values in the broadcast mod % operation to match the bounds on each column.
This small example helps me understand what is going on:
In [123]: N=2
In [124]: np.arange(N**3)[:,None]/[N**2, N, 1]
Out[124]:
array([[ 0. , 0. , 0. ],
[ 0.25, 0.5 , 1. ],
[ 0.5 , 1. , 2. ],
[ 0.75, 1.5 , 3. ],
[ 1. , 2. , 4. ],
[ 1.25, 2.5 , 5. ],
[ 1.5 , 3. , 6. ],
[ 1.75, 3.5 , 7. ]])
So we generate a range of numbers (0 to 7) and divide them by 4,2, and 1.
The rest of the calculation just changes each value without further broadcasting
Apply %N to each element
In [126]: np.arange(N**3)[:,None]/[N**2, N, 1]%N
Out[126]:
array([[ 0. , 0. , 0. ],
[ 0.25, 0.5 , 1. ],
[ 0.5 , 1. , 0. ],
[ 0.75, 1.5 , 1. ],
[ 1. , 0. , 0. ],
[ 1.25, 0.5 , 1. ],
[ 1.5 , 1. , 0. ],
[ 1.75, 1.5 , 1. ]])
Assigning to an int array is the same as converting the floats to integers:
In [127]: (np.arange(N**3)[:,None]/[N**2, N, 1]%N).astype(int)
Out[127]:
array([[0, 0, 0],
[0, 0, 1],
[0, 1, 0],
[0, 1, 1],
[1, 0, 0],
[1, 0, 1],
[1, 1, 0],
[1, 1, 1]])

Create 2-dimensional range

I have a column vector of start values X, and a column vector of end values Z, and I want to create a matrix that creates linspaces between X and Z of size n. Is there a way to generate that directly without iterating?
Say n=10, and Z in this simple example is just a vector of 20. Then, the following code
X = np.arange(0,5,1)
Y = np.empty((5, 10))
for idx in range(0, len(X)):
Y[idx] = np.linspace(X[idx], 20, 10)
generates what I want, but it requires iteration. Is there any more performant solution, or one directly built in without all that do-it-yourself logic?
Here's the expected output for my test case:
Y
array([[ 0. , 2.22222222, 4.44444444, 6.66666667,
8.88888889, 11.11111111, 13.33333333, 15.55555556,
17.77777778, 20. ],
[ 1. , 3.11111111, 5.22222222, 7.33333333,
9.44444444, 11.55555556, 13.66666667, 15.77777778,
17.88888889, 20. ],
[ 2. , 4. , 6. , 8. ,
10. , 12. , 14. , 16. ,
18. , 20. ],
[ 3. , 4.88888889, 6.77777778, 8.66666667,
10.55555556, 12.44444444, 14.33333333, 16.22222222,
18.11111111, 20. ],
[ 4. , 5.77777778, 7.55555556, 9.33333333,
11.11111111, 12.88888889, 14.66666667, 16.44444444,
18.22222222, 20. ]])
That's what np.meshgrid is for. Edit: Nevermind, that's not what you wanted.
Here's what you want:
>>> X = np.arange(0, 5, 1)[:, None]
>>> Y = np.linspace(0, 1, 10)[None, :]
>>> X+Y*(20-X)
array([[ 0. , 2.22222222, 4.44444444, 6.66666667,
8.88888889, 11.11111111, 13.33333333, 15.55555556,
17.77777778, 20. ],
[ 1. , 3.11111111, 5.22222222, 7.33333333,
9.44444444, 11.55555556, 13.66666667, 15.77777778,
17.88888889, 20. ],
[ 2. , 4. , 6. , 8. ,
10. , 12. , 14. , 16. ,
18. , 20. ],
[ 3. , 4.88888889, 6.77777778, 8.66666667,
10.55555556, 12.44444444, 14.33333333, 16.22222222,
18.11111111, 20. ],
[ 4. , 5.77777778, 7.55555556, 9.33333333,
11.11111111, 12.88888889, 14.66666667, 16.44444444,
18.22222222, 20. ]])
List comprehensions at least are faster, and sometimes easier to understand than loops (also, almost always use xrange instead of range, btw):
matrix = np.array([np.linspace(x, 20, 10) for x in X])

Categories

Resources