Python plot_surface set Y limits to center at 0 - numpy

I'm using cylindric coordinates, created a meshgrid and tried to plot using plot_surface. My full dataset doesn't get properly drawn because the scaling on the Y-axis is not correct.
I'm trying to plot the magnetic field values versus Z and P(rho) values. Z can be both negative and positive. P(rho) can only be positive.
The problem is that p (Y-axis) always starts from 0 to 10 (giving 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10) whereas I'd like to have it like: 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 so that its centered at 0.
So this is my example code. I won't add the calculations for the magnetic field. These are not relevant for this issue.
fig = plt.figure()
ax = Axes3D(fig)
# Define the plane over which fields are computed.
# N must be odd to include the point (0,0).
M = 26 # No. of points along the rho axis.
N = 51 # No. of points along the z axis.
p1 = np.linspace(0, a, M)
p = np.concatenate([p1[::-1][:-1], p1]) # Make it symmetric.
z = np.linspace(-d, d, N)
p, z = np.meshgrid(p, z) # Create grid of (p,z).
# CALCULATE magnetic field bt. So assume this is done here...
bt = np.sqrt(np.power(bz, 2) + np.power(bp, 2))
ax.plot_surface(z, p, bt, cmap=plt.cm.YlGnBu_r)
plt.show()
So when making adding the line,
p = np.concatenate([p1[::-1][:-1], p1])
the dimensions are broken and plot_surface is rightfully complaining. I'd expect this line would center the 0 in the middle of the array.
Thanks.

Related

Numpy subarrays and relative indexing

I have been searching if there is an standard mehtod to create a subarray using relative indexes. Take the following array into consideration:
>>> m = np.arange(25).reshape([5, 5])
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14],
[15, 16, 17, 18, 19],
[20, 21, 22, 23, 24]])
I want to access the 3x3 matrix at a specific array position, for example [2,2]:
>>> x = 2, y = 2
>>> m[slice(x-1,x+2), slice(y-1,y+2)]
array([[ 6, 7, 8],
[11, 12, 13],
[16, 17, 18]])
For example for the above somethig like m.subarray(pos=[2,2], shape=[3,3])
I want to sample a ndarray of n dimensions on a specific position which might change.
I did not want to use a loop as it might be inneficient. Scipy functions correlate and convolve do this very efficiently, but for all positions. I am interested only in the sampling of one.
The best answer could solve the issues at edges, in my case I would like for example to have wrap mode:
(a b c d | a b c d | a b c d)
--------------------EDITED-----------------------------
Based on the answer from #Carlos Horn, I could create the following function.
def cell_neighbours(array, index, shape):
pads = [(floor(dim/2), ceil(dim / 2)) for dim in shape]
array = np.pad(self.configuration, pads, "wrap")
views = np.lib.stride_tricks.sliding_window_view
return views(array, shape)[tuple(index)]
Last concern might be about speed, from docs: For many applications using a sliding window view can be convenient, but potentially very slow. Often specialized solutions exist.
From here maybe is easier to get a faster solution.
You could build a view of 3x3 matrices into the array as follows:
import numpy as np
m = np.arange(25).reshape(5,5)
m3x3view = np.lib.stride_tricks.sliding_window_view(m, (3,3))
Note that it will change slightly your indexing on half the window size meaning
x_view = x - 3//2
y_view = y - 3//2
print(m3x3view[x_view,y_view]) # gives your result
In case a copy operation is fine, you could use:
mpad = np.pad(m, 1, mode="wrap")
mpad3x3view = np.lib.stride_tricks.sliding_window_view(mpad, (3,3))
print(mpad3x3view[x % 5,y % 5])
to use arbitrary x, y integer values.

TF broadcast along first axis

Say I have 2 tensors, one with shape (10,1) and another one with shape (10, 11, 1)... what I want is to multiply those broadcasting along the first axis, and not the last one, as used to
tf.zeros([10,1]) * tf.ones([10,12,1])
however this is not working... is there a way to do it without transposing it using perm?
You cannot change the broadcasting rules, but you can prevent broadcasting by doing it yourself. Broadcasting takes effect if the ranks are different.
So instead of permuting the axes, you can also repeat along a new axis:
import tensorflow as tf
import einops as ops
a = tf.zeros([10, 1])
b = tf.ones([10, 12, 1])
c = ops.repeat(a, 'x z -> x y z', y=b.shape[1]) * b
c.shape
>>> TensorShape([10, 12, 1])
For the above example, you need to do tf.zeros([10,1])[...,None] * tf.ones([10,12,1]) to satisfy broadcasting rules: https://numpy.org/doc/stable/user/basics.broadcasting.html#general-broadcasting-rules
If you want to do this for any random shapes, you can do the multiplication with the transposed shape, so that the last dimensions of both the matrices match, obeying broadcasting rule and then do the transpose again, to get back to the required output,
tf.transpose(a*tf.transpose(b))
Example,
a = tf.ones([10,])
b = tf.ones([10,11,12,13,1])
tf.transpose(b)
#[1, 13, 12, 11, 10]
(a*tf.transpose(b))
#[1, 13, 12, 11, 10]
tf.transpose(a*tf.transpose(b)) #Note a is [10,] not [10,1], otherwise you need to add transpose to a as well.
#[10, 11, 12, 13, 1]
Another approach is to expanding the axis:
a = tf.ones([10])[(...,) + (tf.rank(b)-1) * (tf.newaxis,)]

How to show axis ticks corresponding to plotted datapoints in a seaborn plot?

I am using seaborn to plot some values which are numerical. But the each of those numbers correspond to a textual value and I want those textual values to be displayed on the axes. Like if the numerical values progress as 0, 5, 10, ..., 30; each of those encoded numbers must be linked to a textual description. How can I do this?
Main Point:
Use:
ax = plt.gca()
ax.set_xticks([<the x components of your datapoints>]);
ax.set_yticks([<the y components of your datapoints>]);
More elaborate version below.
You can go back to matplotlib and it will do it for you.
Let's say you want to plot [0, 7, 14 ... 35] against [0, 2, 4, ... 10]. The two arrays can be created by:
stepy=7
stepx=2
[stepy*y for y in range(6)]
(returning [0, 7, 14, 21, 28, 35])
and
[stepx*x for x in range(6)]
(returning [0, 2, 4, 6, 8, 10]).
Plot these with seaborn:
sns.scatterplot([stepx*x for x in range(6)],[stepy*y for y in range(6)]).
Give current axis to matplotlib by ax = plt.gca(), finish using set_xticks and set_yticks:
ax.set_xticks([stepx*x for x in range(6)]);
ax.set_yticks([stepy*y for y in range(6)]);
The whole code together:
stepy=7
stepx=2
sns.scatterplot([stepx*x for x in range(6)],[stepy*y for y in range(6)])
ax = plt.gca()
ax.set_xticks([stepx*x for x in range(6)]);
ax.set_yticks([stepy*y for y in range(6)]);
Yielding the plot:
I changed the example in the OP because with those numbers to plot, the plots already behave as desired.

Find indexes of nonuniform sample with np.random.choice

Let's say I have a positions information in the form a two large 1D arrays X and Y. I want to sample non-uniformly positions from these arrays.
I thought I could do this with np.random.choice, but since it only accepts 1D arrays and I cannot do:
Xsample = np.random.choice(X, n, p)
Ysample = np.random.choice(Y, n, p)
with n number of points in the sample, and p a probability array, since this will sample different points for Xsample and Ysample, I am left with finding a way to obtain the indexes of one sampling. The problem is that there is no guarantee that the numbers in the lists are unique so cannot quite use np.where.
Any thoughts?
Doh, I can just sample from the indexes.
Here's a working example:
X = np.array([1, 2, 3, 4, 5])
Y = np.array([11, 12, 13, 14, 15])
p = [0.25, 0., 0.5, 0.25]
sample_idxs = np.random.choice(arange(len(X)), 2, p)
# can also be
# sample_idxs = np.random.choice(len(X), 2, p)
sample_idxs
> array([2, 4])
X[sample_idxs]
> array([3, 5])
Y[sample_idxs]
> array([13, 15])

Need help plot 1 to n y-series with matplotlib

I have a problem that the user of my script want to be able to print 1 - n graphs of the type account (ex 1930,1940 etc) and the sum for every account for every year.
The graph I want to plot should look like this (in this ex 2 accounts(1930 and 1940) and sum for every account for every year):
The input for the graph printing is like this (The user of the script should be able to choose as many accounts as the user wants 1-n):
How many accounts to print graphs for? 2
Account 1 :
1930
Account 2 :
1940
The system will store the Accounts in an array (accounts = [1930,1940]
) and look up the sum for every account for every year. The years and sum for the accounts are placed in a matrix ([[2008, 1, 12], [2009, 7, 30], [2010, 13, 48], [2011, 19, 66], [2012, 25, 84], [2013, 31, 102]]).
When this is done I want to plot 1 - n graphs (in this case 2 graphs). But I can't figure out how to plot with 1 - n accounts...
For the moment I just use this code to print the graph and it's just static :(:
#fix the x serie
x_years = []
for i in range (nrOfYearsInXML):
x_years.append(matrix[x][0])
x = x + 1
plt.xticks(x_years, map(str,x_years))
#fix the y series, how to solve the problem if the user shows 1 - n accounts?
1930_sum = [1, 7, 13, 19, 25, 31]
1940_sum = [12, 30, 48, 66, 84, 102]
plt.plot(x_years, konto1_summa, marker='o', label='1930')
plt.plot(x_years, konto2_summa, marker='o', label='1940')
plt.xlabel('Year')
plt.ylabel('Summa')
plt.title('Sum for account per year')
plt.legend()
plt.show()
Ok, so I have tried with for loops etc, but I have not been able to figure it out with 1-n accounts and an unique account label to 1-n accounts..
My scenario is that the user choose 1 - n accounts. Specify the accounts (ex 1930,1940,1950..). Store the accounts to an array. System calculate the sum for 1-n account for every year and place this data to the matrix. System when reads from the accounts array and the matrix and plot 1-n graphs. Every graph with account label.
A shorter version of the problem...
For example if I have the x values (the years 2008-2013) and the y values (the sum for the accounts for every year) in a matrix and the accounts(should also be used as label) in an array like this:
accounts = [1930,1940]
matrix = [[2008, 1, 12], [2009, 7, 30], [2010, 13, 48], [2011, 19, 66], [2012, 25, 84], [2013, 31, 102]]
Or I can explain x and y like this:
x y1(1930 graph1) y2(1940 graph2)
2008 1 12
2009 7 30
2010 13 48
etc etc etc
The problem for me is that the user can choose one to many accounts (accounts [1..n]) and this will result in 1 to many account graphs.
Any idea how to solve it.. :)?
BR/M
I don't quite understand what you are asking, but I think this is what you want:
# set up axes
fig, ax = plt.subplots(1, 1)
ax.set_xlabel('xlab')
ax.set_ylabel('ylab')
# loop and plot
for j in range(n):
x, y = get_data(n) # what ever function you use to get your data
lab = get_label(n)
ax.plot(x, y, label=lab)
ax.legend()
plt.show()
More concretely, assuming you have the matrix structure you posted above:
# first, use numpy, you have it installed anyway if matplotlib is working
# and it will make your life much nicer
data = np.array(data_list_of_lists)
x = data[:,0]
for j in range(n):
y = data[:, j+1]
ax.plot(x, y, lab=accounts[j])
A better way to do this is to store your data in a dict
data_dict[1940] = (x_data_1940, y_data_1940)
data_dict[1930] = (x_data_1930, y_data_1930)
# ...
for k in acounts:
x,y = data_dict[k]
ax.plot(x, y, lab=k)