Scrollable Bar graph matplotlib - python

I am a newbie to python and am trying to plot a graph for some frame ids, the frame ids can vary from just about 10 in number to 600 or above in number.
Currently, I have this and it works and displays 37 ids together but if I have suppose 500 ids, it clutters them and overlaps the text data. I want to be able to create it in such a way that in one go I only display first 20 ids and there is a scroll bar that displays the next 20 ids and so on..
My code so far:
import matplotlib.pyplot as plt;
import numpy as np
fig,ax=plt.subplots(figsize=(100,2))
x=range(1,38)
y=[1]*len(x)
plt.bar(x,y,width=0.7,align='edge',color='green',ecolor='black')
for i,txt in enumerate(x):
ax.annotate(txt, (x[i],y[i]))
current=plt.gca()
current.axes.xaxis.set_ticks([])
current.axes.yaxis.set_ticks([])
plt.show()
and my output:
enter image description here

Matplotlib provides a Slider widget. You can use this to slice the array to plot and display only the part of the array that is selected.
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
import numpy as np
fig,ax=plt.subplots(figsize=(10,6))
x=np.arange(1,38)
y=np.random.rand(len(x))
N=20
def bar(pos):
pos = int(pos)
ax.clear()
if pos+N > len(x):
n=len(x)-pos
else:
n=N
X=x[pos:pos+n]
Y=y[pos:pos+n]
ax.bar(X,Y,width=0.7,align='edge',color='green',ecolor='black')
for i,txt in enumerate(X):
ax.annotate(txt, (X[i],Y[i]))
ax.xaxis.set_ticks([])
ax.yaxis.set_ticks([])
barpos = plt.axes([0.18, 0.05, 0.55, 0.03], facecolor="skyblue")
slider = Slider(barpos, 'Barpos', 0, len(x)-N, valinit=0)
slider.on_changed(bar)
bar(0)
plt.show()

Related

How to make a ipywidget button update a specific axis of a matplotlib figure?

How can I make a ipywidget button in a Jupyter notebook update a plot in a specific axis?
I already know how to make a button update a plot when using a single axis, like so:
import ipywidgets as widgets
from IPython.display import display
import matplotlib.pyplot as plt
import numpy as np
btn = widgets.Button(description='Click')
display(btn)
output = widgets.Output()
def on_click_fn(obj):
output.clear_output()
values = np.random.rand(10)
with output:
plt.plot(values)
plt.show()
btn.on_click(on_click_fn)
display(output)
In this example, clicking the button updates the plot and shows a new set of 10 random points. I thought it would be simple to extend this to updating a specific axis, and attempted the following:
import ipywidgets as widgets
from IPython.display import display
import matplotlib.pyplot as plt
import numpy as np
btn = widgets.Button(description='Click')
display(btn)
output = widgets.Output()
fig, ax = plt.subplots(ncols=2)
def on_click_fn(obj):
output.clear_output()
values = np.random.rand(10)
with output:
ax[0].plot(values)
plt.show()
btn.on_click(on_click_fn)
display(output)
However, clicking the button in this example does not seem to do anything. I tried different combinations of adding/removing the plt.show() call, using fig.draw() instead, using fig.canvas.draw_idle(), etc, without much success. What's the correct, least "hacky" way of accomplishing this?
Note: This question is only about how to make a button update a plot, like my first example, instead of making the button update a specific axis only.
with this code
import ipywidgets as widgets
from IPython.display import display
import matplotlib.pyplot as plt
import numpy as np
%matplotlib widget
btn = widgets.Button(description='Click')
display(btn)
output = widgets.Output()
fig, ax = plt.subplots(ncols=2)
def on_click_fn(obj):
output.clear_output()
values = np.random.rand(10)
with output:
ax[0].plot(values)
plt.show()
btn.on_click(on_click_fn)
display(output)
I got this output

Plot dual points with one point fixed

I'm trying to plot a set of points with a special feature,
first plot 2 points with a random coordinates x and y, in a range from 0 to 200,
but my problem is how can set this points as fixed or centers, take this center-points and from this points, plot one new point with random coordinates(as pairs of points A-a, B-b, etc), and define the distance that can't be higher than 30 meter or units of distance beetwen this points. To get the points like this
I add part of my code to make this
import matplotlib as mpl
from matplotlib.figure import Figure
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from itertools import product
from matplotlib.lines import Line2D
fig,ax=plt.subplots()
#AP POINTS
###################################################
points_xA=np.random.randint(0,200)
points_yA=np.random.randint(0,200)
points_xB=np.random.randint(0,200)
points_yB=np.random.randint(0,200)
center1=np.array([points_xA,points_yB])
center2=np.array([points_xB,points_yB])
ax.annotate("A",xy=(center1),fontsize=12,bbox={"boxstyle":"circle","color":"orange"})
ax.annotate("B",xy=(center2),fontsize=12,bbox={"boxstyle":"circle","color":"orange"})
#STA POINTS
######################################################
#points_xa=np.random.randint()
#points_ya=np.random.randint()
#points_xb=np.random.randint()
#points_yb=np.random.randint()
######################################################
#LABELS
plt.title('random points')
plt.xlabel('x(m)')
plt.ylabel('y(m)')
plt.xlim(0,210)
plt.ylim(0,210)
plt.grid(True)
plt.show()
i have develop a script that plot points as i wanted, but it have some issues:
1.- The menu or bar where the zoom functions, save image, etc. It disappeared and I can't zoom, which I think would be the most important thing.
2.- The table where is the coordinates of each point, for example, for AP_A it have his STA_A1 o more, depending how many STA's you want( for 3 STA's it would be STA_A1, STA_A2, STA_A3, etc)
but in the table apears as STA_A1, for any STA, in the next image it's more clear
I hope it will be useful to someone, on the other hand if someone can correct those errors in my code it would be great, I thank to this community where I have found some solutions on several occasions.
code:
import matplotlib as mpl
from matplotlib.figure import Figure
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
from itertools import product
from matplotlib.lines import Line2D
##########################
#RADIOS
radius1=30
#radius2=30
#AP POINTS
###################################################
def setNodos(n,rango=300,n_clientes=6):
listaNodos = []
for i in range(n):
points_x=np.random.randint(0,rango)
points_y=np.random.randint(0,rango)
listaNodos.append((np.array([points_x,points_y]),n_clientes))
return listaNodos
listaNodos = setNodos(4,300,3)
abc = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
points_x = []
points_y = []
sta_cant = []
points_sta_x = []
points_sta_y = []
sta_names = []
for nodo,n in listaNodos:
points_x.append(nodo[0])
points_y.append(nodo[1])
sta_cant.append(n)
t_data=[]
########################################
fig = plt.figure(figsize = (15,10))
ax = plt.subplot2grid((3,2), (0, 0),colspan=2,rowspan=2)
l=0
sta_n=0
print(listaNodos)
for centerA,sta_n in listaNodos:
cxA,cyA = centerA
ax.annotate(abc[l],xy=(centerA),fontsize=12,bbox={"boxstyle":"circle","color":"orange"})
#RADIO CIRCULO ROJO
ct1A=np.linspace(0,2*np.pi)
circx11,circy12 = radius1*np.cos(ct1A)+cxA, radius1*np.sin(ct1A)+cyA
plt.plot(circx11, circy12, ls="-",color='red')
#RELLENO CIRCULO ROJO
ax= plt.gca()
t1= plt.Polygon([[i,j] for i, j in zip(circx11,circy12)], color='slategrey', alpha=0.2)
ax.add_patch(t1)
######################################################
#STA POINTS
######################################################
r_sta = np.random.randint(0,radius1,size=sta_n)
tita_sta = np.random.randint(0,359,size=sta_n)
x_sta = np.round(r_sta*np.cos(tita_sta)+cxA,0)
y_sta = np.round(r_sta*np.sin(tita_sta)+cyA,0)
print(x_sta,y_sta)
for x,y in zip(x_sta,y_sta):
#plt.scatter(x,y,c='b',zorder=1000)
x = np.min((300,np.max((0,int(x)))))
y = np.min((300,np.max((0,int(y)))))
ax.annotate(abc[l].lower(),xy=((x,y)),fontsize=10,color='black',
bbox={"boxstyle":"circle","color":"steelblue","alpha":0.5},
)
sta_names.append('STA_%s%i'%(abc[l],l+1))
points_sta_x.append(x)
points_sta_y.append(y)
l+=1
######################################################
#Tabla con coordenadas
plt.xlabel('x(m)')
plt.ylabel('y(m)')
plt.xlim(-10,310)
plt.ylim(-10,310)
ax.grid(True)
plt.title('random points')
t_data.append(points_x+points_sta_x)
t_data.append(points_y+points_sta_y)
print(t_data)
print(sta_n)
collLabels =[('AP_%s'%i) for i in abc[:len(points_x)]]
for name in sta_names:
collLabels.append(name)
print(collLabels)
ax1 = plt.subplot2grid((3,2), (2, 0),colspan=2,rowspan=1)
table=ax1.table(cellText = t_data,
colLabels=collLabels,
rowLabels=('coord_x','coord_y'),
bbox=[0,0.3,0.1+0.05*len(collLabels),0.6+0.01*len(collLabels)]
,cellLoc='center',fontsize=30)
plt.xticks([])
plt.yticks([])
plt.axis('OFF')
plt.tight_layout(rect=[0.01, 0.01, 1.0, 1.0])
#######################################################
#LABELS
ax.set_aspect('equal')
plt.savefig('./salida/escen_random.png')
plt.show()

Python plot with unique colors for more than 10 lines

I am trying to plot the line graph with around 15 to 50 items, colors are repeating that makes the graph not usable.
I have tried the answers from several method in the answers of a similar question like numpy, random.
However, i am unable to find a easy way to do this .
import matplotlib.pyplot as plt
import os
import pandas as pd
import random
from datetime import datetime, timedelta
import matplotlib.dates as dates
import matplotlib.colors as colors
import numpy as np
df2=pd.read_csv("Portperfdetails.csv")
df3 = df2.drop(df2.index[0])
df3['DATETIME'] = pd.to_datetime(df3['DATETIME'])
portname=list(dict.fromkeys(df3['PORT_NAME']))
for i in range(len(portname)):
X = []
Y = []
X = list(df3.loc[df3['PORT_NAME'] == '%s' % portname[i]]['DATETIME'])
Y = list(df3.loc[df3['PORT_NAME'] == '%s' % portname[i]]['TOTAL_MBYTES'])
ax = plt.axes()
ax.xaxis.set_minor_locator(dates.HourLocator(interval=4)) # every 4 hours
ax.xaxis.set_minor_formatter(dates.DateFormatter('%H:%M')) # hours and minutes
ax.xaxis.set_major_locator(dates.DayLocator(interval=1)) # every day
ax.xaxis.set_major_formatter(dates.DateFormatter('\n%d-%m-%Y'))
for i in range(len(Y)):
Y[i] = int(Y[i])
num_plots = 20
plt.plot(X, Y)
plt.ylabel('Port throughput')
plt.xlabel('Time')
plt.savefig('example.png')
Graph
I'll use a toy example since I do not have access to your data (df3).
I adapted this directly from the List of named colors example in the Matplotlib Gallery. The idea is to iterate over color names along with each line that is being plotted and use the color name to specify the color for each line.
from matplotlib import pyplot as plt
import matplotlib.colors as colors
fig, ax = plt.subplots()
lotsa_colors = colors.get_named_colors_mapping()
for cname,i in zip(lotsa_colors,range(50)):
y = [n for n in range(i,i+10)]
#print(cname,lotsa_colors[name])
ax.plot(y,color=lotsa_colors[cname])
plt.show()
#plt.close()
Looks like there are 1163 color names and 1105 unique colors
len(set(lotsa_colors.values()))
If you wanted to you could randomize the color names.
import random
lotsa_colors = colors.get_named_colors_mapping()
lotsa_colors = list(lotsa_colors.keys())
random.shuffle(lotsa_colors)

Change format string of axis picker in matplotlib [duplicate]

Short version: is there a Python method for displaying an image which shows, in real time, the pixel indices and intensities? So that as I move the cursor over the image, I have a continually updated display such as pixel[103,214] = 198 (for grayscale) or pixel[103,214] = (138,24,211) for rgb?
Long version:
Suppose I open a grayscale image saved as an ndarray im and display it with imshow from matplotlib:
im = plt.imread('image.png')
plt.imshow(im,cm.gray)
What I get is the image, and in the bottom right of the window frame, an interactive display of the pixel indices. Except that they're not quite, as the values are not integers: x=134.64 y=129.169 for example.
If I set the display with correct resolution:
plt.axis('equal')
the x and y values are still not integers.
The imshow method from the spectral package does a better job:
import spectral as spc
spc.imshow(im)
Then in the bottom right I now have pixel=[103,152] for example.
However, none of these methods also shows the pixel values. So I have two questions:
Can the imshow from matplotlib (and the imshow from scikit-image) be coerced into showing the correct (integer) pixel indices?
Can any of these methods be extended to show the pixel values as well?
There a couple of different ways to go about this.
You can monkey-patch ax.format_coord, similar to this official example. I'm going to use a slightly more "pythonic" approach here that doesn't rely on global variables. (Note that I'm assuming no extent kwarg was specified, similar to the matplotlib example. To be fully general, you need to do a touch more work.)
import numpy as np
import matplotlib.pyplot as plt
class Formatter(object):
def __init__(self, im):
self.im = im
def __call__(self, x, y):
z = self.im.get_array()[int(y), int(x)]
return 'x={:.01f}, y={:.01f}, z={:.01f}'.format(x, y, z)
data = np.random.random((10,10))
fig, ax = plt.subplots()
im = ax.imshow(data, interpolation='none')
ax.format_coord = Formatter(im)
plt.show()
Alternatively, just to plug one of my own projects, you can use mpldatacursor for this. If you specify hover=True, the box will pop up whenever you hover over an enabled artist. (By default it only pops up when clicked.) Note that mpldatacursor does handle the extent and origin kwargs to imshow correctly.
import numpy as np
import matplotlib.pyplot as plt
import mpldatacursor
data = np.random.random((10,10))
fig, ax = plt.subplots()
ax.imshow(data, interpolation='none')
mpldatacursor.datacursor(hover=True, bbox=dict(alpha=1, fc='w'))
plt.show()
Also, I forgot to mention how to show the pixel indices. In the first example, it's just assuming that i, j = int(y), int(x). You can add those in place of x and y, if you'd prefer.
With mpldatacursor, you can specify them with a custom formatter. The i and j arguments are the correct pixel indices, regardless of the extent and origin of the image plotted.
For example (note the extent of the image vs. the i,j coordinates displayed):
import numpy as np
import matplotlib.pyplot as plt
import mpldatacursor
data = np.random.random((10,10))
fig, ax = plt.subplots()
ax.imshow(data, interpolation='none', extent=[0, 1.5*np.pi, 0, np.pi])
mpldatacursor.datacursor(hover=True, bbox=dict(alpha=1, fc='w'),
formatter='i, j = {i}, {j}\nz = {z:.02g}'.format)
plt.show()
An absolute bare-bones "one-liner" to do this: (without relying on datacursor)
def val_shower(im):
return lambda x,y: '%dx%d = %d' % (x,y,im[int(y+.5),int(x+.5)])
plt.imshow(image)
plt.gca().format_coord = val_shower(ims)
It puts the image in closure so makes sure if you have multiple images each will display its own values.
All of the examples that I have seen only work if your x and y extents start from 0. Here is code that uses your image extents to find the z value.
import numpy as np
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
d = np.array([[i+j for i in range(-5, 6)] for j in range(-5, 6)])
im = ax.imshow(d)
im.set_extent((-5, 5, -5, 5))
def format_coord(x, y):
"""Format the x and y string display."""
imgs = ax.get_images()
if len(imgs) > 0:
for img in imgs:
try:
array = img.get_array()
extent = img.get_extent()
# Get the x and y index spacing
x_space = np.linspace(extent[0], extent[1], array.shape[1])
y_space = np.linspace(extent[3], extent[2], array.shape[0])
# Find the closest index
x_idx= (np.abs(x_space - x)).argmin()
y_idx= (np.abs(y_space - y)).argmin()
# Grab z
z = array[y_idx, x_idx]
return 'x={:1.4f}, y={:1.4f}, z={:1.4f}'.format(x, y, z)
except (TypeError, ValueError):
pass
return 'x={:1.4f}, y={:1.4f}, z={:1.4f}'.format(x, y, 0)
return 'x={:1.4f}, y={:1.4f}'.format(x, y)
# end format_coord
ax.format_coord = format_coord
If you are using PySide/PyQT here is an example to have a mouse hover tooltip for the data
import matplotlib
matplotlib.use("Qt4Agg")
matplotlib.rcParams["backend.qt4"] = "PySide"
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
# Mouse tooltip
from PySide import QtGui, QtCore
mouse_tooltip = QtGui.QLabel()
mouse_tooltip.setFrameShape(QtGui.QFrame.StyledPanel)
mouse_tooltip.setWindowFlags(QtCore.Qt.ToolTip)
mouse_tooltip.setAttribute(QtCore.Qt.WA_TransparentForMouseEvents)
mouse_tooltip.show()
def show_tooltip(msg):
msg = msg.replace(', ', '\n')
mouse_tooltip.setText(msg)
pos = QtGui.QCursor.pos()
mouse_tooltip.move(pos.x()+20, pos.y()+15)
mouse_tooltip.adjustSize()
fig.canvas.toolbar.message.connect(show_tooltip)
# Show the plot
plt.show()
with Jupyter you can do so either with datacursor(myax)or by ax.format_coord.
Sample code:
%matplotlib nbagg
import numpy as np
import matplotlib.pyplot as plt
X = 10*np.random.rand(5,3)
fig,ax = plt.subplots()
myax = ax.imshow(X, cmap=cm.jet,interpolation='nearest')
ax.set_title('hover over the image')
datacursor(myax)
plt.show()
the datacursor(myax) can also be replaced with ax.format_coord = lambda x,y : "x=%g y=%g" % (x, y)
In case you, like me, work on Google Colab, this solutions do not work as Colab disabled interactive feature of images for matplotlib.
Then you might simply use Plotly:
https://plotly.com/python/imshow/
import plotly.express as px
import numpy as np
img_rgb = np.array([[[255, 0, 0], [0, 255, 0], [0, 0, 255]],
[[0, 255, 0], [0, 0, 255], [255, 0, 0]]
], dtype=np.uint8)
fig = px.imshow(img_rgb)
fig.show()
Matplotlib has built-in interactive plot which logs pixel values at the corner of the screen.
To setup first install pip install ipympl
Then use either %matplotlib notebook or %matplotlib widget instead of %matplotlib inline
The drawback with plotly or Bokeh is that they don't work on Pycharm.
For more information take a look at the doc
To get interactive pixel information of an image use the module imagetoolbox
To download the module open the command prompt and write
pip install imagetoolbox
Write the given code to get interactive pixel information of an image
enter image description here
Output:enter image description here

Python Using pyplot slider with subplots

I am quite new to Python, so please excuse if this is a stupid beginner's error. However I am struggling with it for quite some time.
I want to create a figure with n x m subplots, each subplot being np.array of shape [1024,264,264]. As I am looking for differences occuring in the stack along the 0-dimension I want to use a slider to explore all stacks in my figure simultaneously.
The slider instance works nicely for a figure with one subplot but I can't bring them all to work.
That's the code I am using:
import os
from matplotlib import pyplot as plt
import numpy as np
import glob
import h5py
#Define the xy size of the mapped array
xsize=3
ysize=3
lengthh5=9
readlist=[]
for i in range (0,lengthh5):
npraw=np.random.rand(200,50,50)
readlist.append (npraw)
''' Slider visualization'''
from matplotlib.widgets import Slider
fig=plt.figure()
for k in range (0,lengthh5):
ax=fig.add_subplot(xsize,ysize,k)
frame = 10
l = ax.imshow(readlist[k][frame,:,:])
plt.axis('off')
sframe = Slider(fig.add_subplot(50,1,50), 'Frame', 0, len(readlist[0])-1, valinit=0)
def update(val):
frame = np.around(sframe.val)
l.set_data(readlist[k][frame,:,:])
sframe.on_changed(update)
plt.show()
For this particular case I stripped it down to a 3x3 array for my figure and just create randmom (smaller) arrays.
The slider is interestinly only operable on the second last subplot. However I have no real idea how to link it to all subplots simulatenously. Perhaps someone has an idea how to do this.
Thanks a lot in advance,
Tilman
You need to store each imshow AxesImage in a list and inside update, loop over all of them and update each based on the slider,
import os
from matplotlib import pyplot as plt
from matplotlib.widgets import Slider
import numpy as np
import glob
import h5py
#Define the xy size of the mapped array
xsize=3
ysize=3
lengthh5=9
readlist=[]
for i in range (0,lengthh5):
npraw=np.random.rand(200,50,50)
readlist.append (npraw)
fig=plt.figure()
ls = []
for k in range (0,lengthh5):
ax=fig.add_subplot(xsize,ysize,k)
frame = 10
l = ax.imshow(readlist[k][frame,:,:])
ls.append(l)
plt.axis('off')
sframe = Slider(fig.add_subplot(50,1,50), 'Frame',
0, len(readlist[0])-1, valinit=0)
def update(val):
frame = np.around(sframe.val)
for k, l in enumerate(ls):
l.set_data(readlist[k][frame,:,:])
sframe.on_changed(update)
plt.show()

Categories

Resources