Related
Given a numpy array with multiple arrays inside, how do I replace all the values of the array with values from another array?
For example:
import numpy
first_array = numpy.array([[1,2],[3,4],[5,6],[7,8],[9,10]])
second_array = numpy.array([0.1, 0.2, 0.3, 0.4, 0.5, 0.6,
0.7, 0.8, 0.9, 1])
Given these arrays, How do I replace 1,2 with 0.1, 0.2 and etc?
Use np.reshape
# import numpy as np
>>> m
array([[ 1, 2],
[ 3, 4],
[ 5, 6],
[ 7, 8],
[ 9, 10]])
>>> n
array([0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1. ])
>>> n.reshape(m.shape)
array([[0.1, 0.2],
[0.3, 0.4],
[0.5, 0.6],
[0.7, 0.8],
[0.9, 1. ]])
first_array = np.array([[1,2],[3,4],[5,6],[7,8],[9,10]])
second_array = np.array([0.1, 0.2, 0.3, 0.4, 0.5, 0.6,0.7, 0.8, 0.9, 1])
np.set_printoptions(formatter={'float':"{0:0.1f}".format})
first_array = first_array.astype(float)
for i in range(np.shape(first_array)[0]):
for j in range(np.shape(first_array)[1]):
first_array[i][j] = second_array[2*i+j]
print(first_array)
Output:
[[0.1 0.2]
[0.3 0.4]
[0.5 0.6]
[0.7 0.8]
[0.9 1.0]]
I have a plot with two different series of curves which I am going to plot them using points and lines. I would like to have a legend such that the line and point markers share the same label.
I have tried this suggestion which works well if my both series of plots have different point types, instead of line and points.
The code that I am currently using, with improper legend, is
import numpy as np
import matplotlib.pyplot as plt
Vs = np.array([0.5, 1, 1.5, 2])
Xs = np.array([[ 0.5, 0.2, 0.7],
[ 0.5, 0.3, 0.9],
[ 0.5, 0.5, 0.4],
[ 0.5, 0.7, 0.4],
[ 0.5, 0.9, 0.7],
[ 1, 0.15, 0.9],
[ 1, 0.35, 0.6],
[ 1, 0.45, 0.6],
[ 1, 0.67, 0.5],
[ 1, 0.85, 0.9],
[ 1.5, 0.1, 0.9],
[ 1.5, 0.3, 0.7],
[ 1.5, 0.76, 0.3],
[ 1.5, 0.98, 0.4],
[ 2, 0.21, 0.5],
[ 2, 0.46, 0.4],
[ 2, 0.66, 0.3],
[ 2, 0.76, 0.5],
[ 2, 0.88, 0.4],
[ 2, 0.99, 0.4]])
f, axs = plt.subplots(1, 1, figsize=(2.5,3))
#-------------------------------------
axs.set_xlim(0.38,1.0)
axs.set_ylim(0.0,4.0)
colors = plt.cm.gist_ncar(np.linspace(0,1,max(Vs)+3))
for idx,Val in enumerate(Vs):
axs.plot(Xs[Xs[:,0] == Val ,1], Xs[Xs[:,0] == Val ,2],'s',label=r"$Y={}$".format(Val), ms=3, color=colors[idx])
axs.plot(Xs[Xs[:,0] == Val ,1], Xs[Xs[:,0] == Val ,2]*Val/0.3,'-', label=r"$Y={}$".format(Val), ms=3, color=colors[idx])
axs.set_ylim(0.0,4.0)
axs.set_ylabel(r"$Y$ ", labelpad=2)
axs.set_xlabel(r"$X$ ", labelpad=2)
axs.set_yticks([0,0.5,1.0,1.5,2.0, 2.5, 3.0, 3.5, 4.0])
axs.set_xticks([0,0.5,1.0])
axs.legend(fontsize=6, loc=2, numpoints = 1, labelspacing=0.2,handletextpad=0.2, frameon=False)
f.savefig("tmp.pdf")
plt.show()
Do you any suggestions to resolve this issue?
Applying my answer to How to create two legend objects for a single plot instance? in this case:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
Vs = np.array([0.5, 1, 1.5, 2])
Xs = np.array([[ 0.5, 0.2, 0.7], [ 0.5, 0.3, 0.9], [ 0.5, 0.5, 0.4],
[ 0.5, 0.7, 0.4],[ 0.5, 0.9, 0.7], [ 1, 0.15, 0.9],
[ 1, 0.35, 0.6], [ 1, 0.45, 0.6], [ 1, 0.67, 0.5],
[ 1, 0.85, 0.9], [ 1.5, 0.1, 0.9], [ 1.5, 0.3, 0.7],
[ 1.5, 0.76, 0.3], [ 1.5, 0.98, 0.4], [ 2, 0.21, 0.5],
[ 2, 0.66, 0.3], [ 2, 0.76, 0.5], [ 2, 0.88, 0.4],
[ 2, 0.99, 0.4]])
f, axs = plt.subplots(1, 1, figsize=(2.5,3))
axs.set_xlim(0.38,1.0)
axs.set_ylim(0.0,4.0)
colors = plt.cm.gist_ncar(np.linspace(0,1,max(Vs)+3))
for idx,Val in enumerate(Vs):
axs.plot(Xs[Xs[:,0] == Val ,1], Xs[Xs[:,0] == Val ,2],'s',label=r"$Y={}$".format(Val), ms=3, color=colors[idx])
axs.plot(Xs[Xs[:,0] == Val ,1], Xs[Xs[:,0] == Val ,2]*Val/0.3,'-', label=r"$Y={}$".format(Val), ms=3, color=colors[idx])
axs.set_ylim(0.0,4.0)
axs.set_ylabel(r"$Y$ ", labelpad=2)
axs.set_xlabel(r"$X$ ", labelpad=2)
axs.set_yticks([0,0.5,1.0,1.5,2.0, 2.5, 3.0, 3.5, 4.0])
axs.set_xticks([0,0.5,1.0])
h, l = axs.get_legend_handles_labels()
axs.legend(handles=zip(h[::2], h[1::2]), labels=l[::2],
handler_map = {tuple: matplotlib.legend_handler.HandlerTuple(None)})
plt.show()
I would go with creating custom lines to be shown in your legend. You can go about it by saving the output of each plot command (a line plot returns a matplotlib.lines.Line2D object which stores the line style, marker style, color, etc). You can then loop over the saved lines and create new Line2D objects that combine the properties of the two lines with the same color. Saving these new Line2D objects in a list, say handles, you can then pass that list to the ax.legend() call:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.lines import Line2D
Vs = np.array([0.5, 1, 1.5, 2])
Xs = np.array([[ 0.5, 0.2, 0.7],
[ 0.5, 0.3, 0.9],
[ 0.5, 0.5, 0.4],
[ 0.5, 0.7, 0.4],
[ 0.5, 0.9, 0.7],
[ 1, 0.15, 0.9],
[ 1, 0.35, 0.6],
[ 1, 0.45, 0.6],
[ 1, 0.67, 0.5],
[ 1, 0.85, 0.9],
[ 1.5, 0.1, 0.9],
[ 1.5, 0.3, 0.7],
[ 1.5, 0.76, 0.3],
[ 1.5, 0.98, 0.4],
[ 2, 0.21, 0.5],
[ 2, 0.46, 0.4],
[ 2, 0.66, 0.3],
[ 2, 0.76, 0.5],
[ 2, 0.88, 0.4],
[ 2, 0.99, 0.4]])
f, axs = plt.subplots(1, 1, figsize=(2.5,3))
#-------------------------------------
axs.set_xlim(0.38,1.0)
axs.set_ylim(0.0,4.0)
colors = plt.cm.gist_ncar(np.linspace(0,1,max(Vs)+3))
##saving the Line2D objects:
lines = []
points = []
for idx,Val in enumerate(Vs):
point, = axs.plot(
Xs[Xs[:,0] == Val ,1], Xs[Xs[:,0] == Val ,2],'s',
label=r"$Y={}$".format(Val), ms=3, color=colors[idx]
)
line, = axs.plot(
Xs[Xs[:,0] == Val ,1], Xs[Xs[:,0] == Val ,2]*Val/0.3,'-',
label=r"$Y={}$".format(Val), ms=3, color=colors[idx]
)
points.append(point)
lines.append(line)
axs.set_ylim(0.0,4.0)
axs.set_ylabel(r"$Y$ ", labelpad=2)
axs.set_xlabel(r"$X$ ", labelpad=2)
axs.set_yticks([0,0.5,1.0,1.5,2.0, 2.5, 3.0, 3.5, 4.0])
axs.set_xticks([0,0.5,1.0])
#axs.legend(fontsize=6, loc=2, numpoints = 1, labelspacing=0.2,handletextpad=0.2, frameon=False)
#f.savefig("tmp.pdf")
##generating the legend handles, with linestyle, markerstyle, color, and label
##copied from the plotted lines:
handles = [
Line2D(
[],[], marker=point.get_marker(), linestyle=line.get_linestyle(),
color = line.get_color(),
label = line.get_label(),
) for line, point in zip(lines,points)
]
##passing handles as argument to the `legend()` call:
axs.legend(
handles=handles,
fontsize=6, loc=2, numpoints = 1, labelspacing=0.2,
handletextpad=0.2, frameon=False,
)
plt.show()
The resulting picture looks like this:
EDIT:
Following the example that is linked in the question, one can design a handler object that generates the wanted legend handles. Replacing the last part of the above code with the following:
##a dedicated class that holds the lines to be included in the legend entry
class LineContainer:
def __init__(self, *args):
args = [line for line in args if isinstance(line,Line2D)]
if len(args) < 0:
raise ValueError('At least one line must be passed')
self._lines = list(args)
def get_lines(self):
return self._lines
def get_label(self):
##assuming here that all lines have the same label
return self._lines[0].get_label()
##adapted from https://stackoverflow.com/a/31530393/2454357
class data_handler(object):
def legend_artist(self, legend, orig_handle, fontsize, handlebox):
scale = fontsize / 22
x0, y0 = handlebox.xdescent, handlebox.ydescent
width, height = handlebox.width, handlebox.height
##use these two lines to control the lengths of the individual line
##segments and the spacing between them:
##width for individual artists
l = 0.7*width/len(orig_handle.get_lines())
##distance between individual artists
l0 = 0.3*width/len(orig_handle.get_lines())
result = []
for i, line in enumerate(orig_handle.get_lines()):
new_line = Line2D([],[])
new_line.update_from(line)
##if no linestyle is defined, plot only the marker:
if new_line.get_linestyle() in ['None', None]:
new_line.set_data(
[x0+l*(i+0.5)], [y0+height/2]
)
##else plot markers and lines:
else:
new_line.set_data(
[x0+l*i+l0/2, x0+l*(i+1)-l0/2],
[y0+height/2, y0+height/2]
)
new_line.set_transform(handlebox.get_transform())
handlebox.add_artist(new_line)
result.append(new_line)
return result
##generating the handles
handles = [
LineContainer(line, point) for line, point in zip(lines, points)
]
axs.legend(
handles = handles,
handler_map={LineContainer: data_handler()},
fontsize=6, loc=2, numpoints = 1, labelspacing=0.2,
handletextpad=0.2, frameon=False,
)
plt.show()
gives the following image:
I have two Tensors like this:
template = tf.convert_to_tensor([[1, 0, 0.5, 0.5, 0.3, 0.3],
[1, 0, 0.75, 0.5, 0.3, 0.3],
[1, 0, 0.5, 0.75, 0.3, 0.3],
[1, 0, 0.75, 0.75, 0.3, 0.3]])
patch = tf.convert_to_tensor([[0, 1, 0.43, 0.17, 0.4, 0.4],
[0, 1, 0.18, 0.22, 0.53, 0.6]])
Now I would like to update the second and the last rows of the template with the patch rows to get a value like this:
[[1. 0. 0.5 0.5 0.3 0.3 ]
[0. 1. 0.43 0.17 0.4 0.4 ]
[1. 0. 0.5 0.75 0.3 0.3 ]
[0. 1. 0.18 0.22 0.53 0.6 ]]
With tf.scatter_update it is easy:
var_template = tf.Variable(template)
var_template = tf.scatter_update(var_template, [1, 3], patch)
However, it requires creating a variable. Is there a way to obtain the value using only tensor operations?
I was thinking about tf.where, but then I probably have to broadcast every patch row into the template size and call tf.where for each row.
This one should work. A bit twisted, but no variable used.
import tensorflow as tf
template = tf.convert_to_tensor([[1, 1, 0.5, 0.5, 0.3, 0.3],
[2, 2, 0.75, 0.5, 0.3, 0.3],
[3, 3, 0.5, 0.75, 0.3, 0.3],
[4, 4, 0.75, 0.75, 0.3, 0.3]])
patch = tf.convert_to_tensor([[1, 1, 1, 0.17, 0.4, 0.4],
[3, 3, 3, 0.22, 0.53, 0.6]])
ind = tf.constant([1,3])
rn_t = tf.range(0, template.shape[0])
def index1d(t, val):
return tf.reduce_min(tf.where(tf.equal([t], val)))
def index1dd(t,val):
return tf.argmax(tf.cast(tf.equal(t,val), tf.int64), axis=0)
r = tf.map_fn(lambda x: tf.where(tf.equal(index1d(ind, x), 0), patch[index1dd(ind, x)] , template[x]), rn_t, dtype=tf.float32)
with tf.Session() as sess:
print(sess.run([r]))
I will add here also my solution. This utility function works pretty much the same as scatter_update, but without using Variables:
def scatter_update_tensor(x, indices, updates):
'''
Utility function similar to `tf.scatter_update`, but performing on Tensor
'''
x_shape = tf.shape(x)
patch = tf.scatter_nd(indices, updates, x_shape)
mask = tf.greater(tf.scatter_nd(indices, tf.ones_like(updates), x_shape), 0)
return tf.where(mask, patch, x)
I would like to plot a histogram with a non-uniform x-axis using Matplotlib.
For example, consider the following histogram:
import matplotlib.pyplot as plt
values = [0.68, 0.28, 0.31, 0.5, 0.25, 0.5, 0.002, 0.13, 0.002, 0.2, 0.3, 0.45,
0.56, 0.53, 0.001, 0.44, 0.008, 0.26, 0., 0.37, 0.03, 0.002, 0.19, 0.18,
0.04, 0.31, 0.006, 0.6, 0.19, 0.3, 0., 0.46, 0.2, 0.004, 0.06, 0.]
plt.hist(values)
plt.show()
The first bin has high density, so I would like to zoom in there.
Ideally, I would like to change the values in the x-axis to something like [0, 0.005, 0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1], keeping the bin widths constant within the graph (but not numerically, of course). Is there an easy way to achieve this?
Any comments or suggestions are welcome.
Using bins will solve the problems. The bins are the values to which you assign the values for example 0.28 will be assigned to bin 0.3. The code below provides you an example of using bins:
import matplotlib.pyplot as plt
values = [0.68, 0.28, 0.31, 0.5, 0.25, 0.5, 0.002, 0.13, 0.002, 0.2, 0.3, 0.45,
0.56, 0.53, 0.001, 0.44, 0.008, 0.26, 0., 0.37, 0.03, 0.002, 0.19, 0.18,
0.04, 0.31, 0.006, 0.6, 0.19, 0.3, 0., 0.46, 0.2, 0.004, 0.06, 0.]
plt.hist(values, bins=[0, 0.005, 0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1])
plt.show()
To plot it in a more suitable way, it can be handy to convert the x axis into a logaritmic scale:
plt.hist(values, bins=[0, 0.005, 0.01, 0.02, 0.05, 0.1, 0.2, 0.5, 1], log=True)
changes the log scale on the y axis. Adding the following line to your code will make a logaritmic x axis for your histogram:
plt.xscale('log')
The solution from André is nice, but the bin widths are not constant. Working with a log2 x-axis suits what I was looking for. I use np.logspace to make the bin widths constant in the graph.
That's what I ended up doing:
import matplotlib.pyplot as plt
values = [0.68, 0.28, 0.31, 0.5, 0.25, 0.5, 0.002, 0.13, 0.002, 0.2, 0.3, 0.45,
0.56, 0.53, 0.001, 0.44, 0.008, 0.26, 0., 0.37, 0.03, 0.002, 0.19, 0.18,
0.04, 0.31, 0.006, 0.6, 0.19, 0.3, 0., 0.46, 0.2, 0.004, 0.06, 0.]
bins = np.logspace(-10, 1, 20, base=2)
bins[0]=0
fig, ax = plt.subplots()
plt.hist(values, bins=bins)
ax.set_xscale('log', basex=2)
ax.set_xlim(2**-10, 1)
plt.show()
I have two 2d numpy array (images).
First one defined by image, is storing the sum of a movement at the pixel (i,j)
Second one define by nbCameras, is storing the number of cameras who can see a movement at this pixel (i,j)
I want to create a third image imgFinal which only store the value of the pixel (i,j) and it's neighbours (3 x 3) mask, if the number of cameras who can see the pixel (i,j) is greater than 1.
For now I'm using two for loops which is not the best way. I'd like to increase the speed of the computation but I didn't find the best way to do it yet.
Also I'm a bit blocked as the fact I want to converse the neighbours of the pixel (i, j)
I also tried to use bumpy.vectorize but i can keep the neighbours of my pixel in this case.
What would be the best way to increase the speed of this function?
Thanks for your help!
maskWidth = 3
dstCenterMask = int( (maskWidth - 1) / 2)
imgFinal = np.zeros((image.shape),dtype = np.float32)
for j in range(dstCenterMask,image.shape[0] - dstCenterMask):
for i in range(dstCenterMask,image.shape[1] - dstCenterMask):
if nbCameras[j,i] > 1
imgFinal[j - dstCenterMask : j + dstCenterMask + 1, i - dstCenterMask : i + dstCenterMask + 1] =
image[j - dstCenterMask : j + dstCenterMask + 1, i - dstCenterMask : i + dstCenterMask + 1]
This got quite elegant using skimage.morphology's binary_dilation function. It will take a binary array, and kinda expand any pixels that are true into a 3x3 grid of true values (or any other size). This should also handle cases at the edges. Which i think your implementation did not.
Using this mask it's quite easy to calculate imgFinal
from skimage.morphology import binary_dilation, square
mask = binary_dilation(nbCameras > 1, square(maskWidth))
imgFinal = np.where(mask, image, 0)
square(3) is just shorthand for np.ones((3,3))
http://scikit-image.org/docs/dev/api/skimage.morphology.html?highlight=dilation#skimage.morphology.dilation
Example use of dilation for better explenation of what it does:
In [27]: a
Out[27]:
array([[ 1., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0.],
[ 0., 0., 0., 0., 0.],
[ 0., 0., 0., 1., 0.],
[ 0., 0., 0., 0., 0.]])
In [28]: binary_dilation(a, square(3))
Out[28]:
array([[1, 1, 0, 0, 0],
[1, 1, 0, 0, 0],
[0, 0, 1, 1, 1],
[0, 0, 1, 1, 1],
[0, 0, 1, 1, 1]], dtype=uint8)
Option 1: Try to rewrite the code in a vectorized way. You could convolve with a 3x3 mask like this:
import numpy as np
from scipy.signal import convolve2d
image = np.random.random((100,100))
nbCameras = np.abs(np.random.normal(size=(100,100)).round())
maskWidth = 3
mask = np.ones((maskWidth, maskWidth))
visibilityMask = (nbCameras>1).astype(np.float)
visibilityMask = convolve2d(visibilityMask, mask, mode="same").astype(np.bool)
imgFinal = image.copy()
imgFinal[~visibilityMask] *= 0
import matplotlib.pyplot as plt
for i, (im, title) in enumerate([(image, "image"),
(nbCameras, "nbCameras"),
(visibilityMask, "visibilityMask"),
(imgFinal, "imgFinal")]):
plt.subplot(2,2,i+1)
plt.title(title)
plt.imshow(im, cmap=plt.cm.gray)
plt.show()
This will result in this plot:
Option 2: Use Numba. This uses an advanced just-in-time optimization technique and is specifically useful for speeding up loops.
This doesn't handle cameras on the edge of the array, but neither does your code:
import numpy as np
from numpy.lib.stride_tricks import as_strided
rows, cols, mask_width = 10, 10, 3
mask_radius = mask_width // 2
image = np.random.rand(rows, cols)
nb_cameras = np.random.randint(3 ,size=(rows, cols))
image_view = as_strided(image, shape=image.shape + (mask_width, mask_width),
strides=image.strides*2)
img_final = np.zeros_like(image)
img_final_view = as_strided(img_final,
shape=img_final.shape + (mask_width, mask_width),
strides=img_final.strides*2)
copy_mask = nb_cameras[mask_radius:-mask_radius,
mask_radius:-mask_radius] > 1
img_final_view[copy_mask] = image_view[copy_mask]
After running the above code:
>>> nb_cameras
array([[0, 2, 1, 0, 2, 0, 1, 2, 1, 0],
[0, 1, 1, 1, 1, 2, 1, 1, 2, 1],
[1, 2, 2, 2, 1, 2, 1, 0, 2, 0],
[0, 2, 2, 0, 1, 2, 1, 0, 1, 0],
[1, 2, 0, 1, 2, 0, 1, 0, 0, 2],
[2, 0, 1, 1, 1, 1, 1, 1, 0, 1],
[1, 0, 2, 2, 0, 1, 1, 1, 0, 0],
[0, 0, 1, 0, 1, 0, 1, 0, 2, 2],
[0, 1, 0, 1, 1, 2, 1, 1, 2, 2],
[2, 2, 0, 1, 0, 0, 1, 2, 1, 0]])
>>> np.round(img_final, 1)
array([[ 0. , 0. , 0. , 0. , 0.7, 0.5, 0.6, 0.5, 0.6, 0.9],
[ 0.1, 0.6, 1. , 0.2, 0.3, 0.6, 0. , 0.2, 0.9, 0.9],
[ 0.2, 0.3, 0.3, 0.5, 0.2, 0.3, 0.4, 0.1, 0.7, 0.5],
[ 0.9, 0.1, 0.7, 0.8, 0.2, 0.9, 0.9, 0.1, 0.3, 0.3],
[ 0.8, 0.8, 1. , 0.9, 0.2, 0.5, 1. , 0. , 0. , 0. ],
[ 0.2, 0.3, 0.5, 0.4, 0.6, 0.2, 0. , 0. , 0. , 0. ],
[ 0. , 0.2, 1. , 0.2, 0.8, 0. , 0. , 0.7, 0.9, 0.6],
[ 0. , 0.2, 0.9, 0.9, 0.3, 0.4, 0.6, 0.6, 0.3, 0.6],
[ 0. , 0. , 0. , 0. , 0.8, 0.8, 0.1, 0.7, 0.4, 0.4],
[ 0. , 0. , 0. , 0. , 0. , 0.5, 0.1, 0.4, 0.3, 0.9]])
Another option, to manage the edges, is to use a convolution function from scipy.ndimage:
import scipy.ndimage
mask = scipy.ndimage.convolve(nb_cameras > 1, np.ones((3,3)),
mode='constant') != 0
img_final[mask] = image[mask]
>>> np.round(img_final, 1)
array([[ 0.6, 0.8, 0.7, 0.9, 0.7, 0.5, 0.6, 0.5, 0.6, 0.9],
[ 0.1, 0.6, 1. , 0.2, 0.3, 0.6, 0. , 0.2, 0.9, 0.9],
[ 0.2, 0.3, 0.3, 0.5, 0.2, 0.3, 0.4, 0.1, 0.7, 0.5],
[ 0.9, 0.1, 0.7, 0.8, 0.2, 0.9, 0.9, 0.1, 0.3, 0.3],
[ 0.8, 0.8, 1. , 0.9, 0.2, 0.5, 1. , 0. , 0.3, 0.8],
[ 0.2, 0.3, 0.5, 0.4, 0.6, 0.2, 0. , 0. , 0.7, 0.6],
[ 0.2, 0.2, 1. , 0.2, 0.8, 0. , 0. , 0.7, 0.9, 0.6],
[ 0. , 0.2, 0.9, 0.9, 0.3, 0.4, 0.6, 0.6, 0.3, 0.6],
[ 0.4, 1. , 0.8, 0. , 0.8, 0.8, 0.1, 0.7, 0.4, 0.4],
[ 0.9, 0.5, 0.8, 0. , 0. , 0.5, 0.1, 0.4, 0.3, 0.9]])