I'm trying to write a program that takes in two arrays, one for y values and the other one for the bins, and generate a histogram that put the y values into the corresponding bins' range.
e.g. y = 0.5 when 0 < x< 0.1, y = 0.2 when 0.1 < x <0.2
I cloudn't find any ways to sepcify the y-values in histogram with matplotlib, or is it possible to do so ?
Ps. Can I use bar chart to do so ? but I didn't find a way to specify bins in bar chart , Thanks!
I think you need a bar plot, where you can specify the x values and "height". For instance:
import matplotlib.pyplot as plt
import numpy as np
a=np.array([1,2,3])
b=np.array([4,5,6])
plt.bar(a,b)
plt.show()
For example, you can fill the arrays with an if condition:
a=np.array([0.01,0.15,0.18,0.05,0.15])
b=np.zeros(len(a))
for i in range(len(a)):
if a[i] > 0. and a[i] < 0.1:
b[i] = 0.5
elif a[i] > 0.1 and a[i] < 0.2:
b[i] = 0.2
Related
I'm attempting to plot 2 pairs of (x,y) data and show how much distance is between them.
I have 2 issues with the plot it stands:
When the data points fall on the axis they are being draw behind them, I'd prefer them in front (red data point above).
The text annotation is fixed where it's drawn, this means either the data or the legend can cover it when data points are in the top right or top left quadrants.
The desired output would be a draw order of Axes -> Scatter -> Quiver and then for the text annotation to be drawn in whichever quadrant is not occupied by a data point or the legend.
For issue 1 I've tried combinations of clipon=True and zorder= for all the plot elements but can't seem to bring them in front.
For issue 2 I've considered checking which quadrants the data points are in, draw the legend and check which quadrant that is in and then finally draw the annotation in the remaining unoccupied quadrant(s). However I've struggled to get the correct legend position with legend.get_window_extent() and was hoping there was an easier method of moving the annotation, similar to rcParams["legend.loc"]='Best'. I can't see anything obvious at https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.text.html
Any help would be greatly appreciated, below is the code used to produce the plot. Thanks!
#!/usr/bin/env python3
import matplotlib
matplotlib.use('qt5agg')
import matplotlib.pyplot as plt
data = [[-0.4, 0.4], [0.2, -0.01]]
#data = [[0.4, 0.4], [0.2, -0.01]]
fig, ax = plt.subplots(figsize=(4.5, 3.25), num="Stack Example")
x, y = (zip(*data))
dx = x[1]-x[0]
dy = y[1]-y[0]
c = [0, 1]
scatter = ax.scatter(x, y, c=c, cmap='rainbow', s=250, marker="o")
legend = ax.legend(*scatter.legend_elements(),
title="Legend", fontsize=8, title_fontsize=8)
ax.add_artist(legend)
ax.quiver(x[0], y[0], dx, dy, angles='xy', scale_units='xy', scale=1, headwidth=3)
textstr = '\n'.join((
r'$dx$=%.2f mm' % (dx),
r'$dy$=%.2f mm' % (dy)))
ax.text(0.04, 0.95, textstr, transform=ax.transAxes, fontsize=9, verticalalignment='top')
ax.spines[['left', 'bottom']].set_position('zero')
ax.spines[['top', 'right']].set_visible(False)
ax.set_xlim([-0.5, 0.5])
ax.set_ylim([-0.5, 0.5])
ax.set_xticks([-0.5, -0.25, 0.25, 0.5])
ax.set_yticks([-0.5, -0.25, 0.25, 0.5])
ax.set_xlabel('$x$ $/$ $mm$', fontsize=9)
ax.xaxis.set_label_coords(1.0, 0.4)
ax.set_ylabel('$y$ $/$ $mm$', fontsize=9)
ax.yaxis.set_label_coords(0.57, 1.0)
plt.xticks(fontsize=9)
plt.yticks(fontsize=9)
plt.tight_layout()
fig.canvas.toolbar.setVisible(False)
plt.show()
UPDATE
I've fixed issue 2 as I mentioned above, it's not pretty but works for each of the usage cases I've tried so far.
def get_quadrants(data):
quadrants = []
for datapoint in data:
x = datapoint[0]
y = datapoint[1]
if x < 0 and y < 0:
quadrants.append(2)
elif x < 0 and y > 0:
quadrants.append(0)
elif x > 0 and y < 0:
quadrants.append(3)
else:
quadrants.append(1)
text_quadrant = max(sorted(set((range(4))) - set(quadrants)))
if len(set([2, 3]) - set(quadrants)) == 0:
text_quadrant = 0
if text_quadrant == 0:
x, y = 0.0, 0.95
elif text_quadrant == 1:
x, y = 0.75, 0.95
elif text_quadrant == 2:
x, y = 0.0, 0.15
else:
x, y = 0.75, 0.15
return x, y
I am trying to plot a bar graph using a dataframe, and I used the below code:
def add_line(ax, xpos, ypos):
line = plt.Line2D([xpos, xpos], [ypos + .1, ypos],
transform=ax.transAxes, color='gray')
line.set_clip_on(False)
ax.add_line(line)
def label_len(my_index,level):
labels = my_index.get_level_values(level)
return [(k, sum(1 for i in g)) for k,g in groupby(labels)]
def label_group_bar_table(ax, df):
ypos = -.1
scale = 1./df.index.size
for level in range(df.index.nlevels)[::-1]:
pos = 0
for label, rpos in label_len(df.index,level):
lxpos = (pos + .5 * rpos)*scale
ax.text(lxpos, ypos, label, ha='center', transform=ax.transAxes)
add_line(ax, pos*scale, ypos)
pos += rpos
add_line(ax, pos*scale , ypos)
ypos -= .1
from matplotlib.pyplot import figure
ax = my_df.plot(kind='bar')
ax.set_xticklabels('State')
ax.set_xlabel('Electricity consumed by every resource')
ax.plot([1,2,3])
#plt.xticks(rotation=90)
label_group_bar_table(ax, my_df)
My question is: How do I change the size of the plot and how can I make sure that the ticks are displayed vertically on the x-axis and ensure that the title of the x-axis and the ticks on the x-axis don't overlap?
While using 'figure', I know that the 'rotation' parameter can be changed to 90 to ensure that x ticks are vertical. I also understand that the 'figsize' can be used to set the size while using figure. But I am not sure how we should work with 'ax'.
Why are my y-axis ticks in decimal and what is that 'le11'? My data contains numbers that are 7 digit or 8 digits. Is there a way to ensure the y-axis also contains 7 or 8 digit numbers instead?
My graph looks like:
I am trying to create an image where the x axis is the width, and y axis is the height of the image. And where each point can be given a color based on a RBG mapping. From looking at imshow() from Matplotlib I guess I need to create a meshgrid on the form (NxMx3) where 3 is a tuple or something similar with the rbg colors.
But so far I have not managed to understand how to do that. Lets say I have this example:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap
x_min = 1
x_max = 5
y_min = 1
y_max = 5
Nx = 5 #number of steps for x axis
Ny = 5 #number of steps for y axis
x = np.linspace(x_min, x_max, Nx)
y = np.linspace(y_min, y_max, Ny)
#Can then create a meshgrid using this to get the x and y axis system
xx, yy = np.meshgrid(x, y)
#imagine I have some funcion that does someting based on the x and y values
def somefunc(x_value, y_value):
#do something and return rbg based on that
return x_value + y_value
res = somefunc(xx, yy)
cmap = LinearSegmentedColormap.from_list('mycmap', ['white', 'blue', 'black'])
plt.figure(dpi=100)
plt.imshow(res, cmap=cmap, interpolation='bilinear')
plt.show()
And this creates a plot, but what would I have to do if my goal was to give spesific rbg values based on x and y values inside somefunc and make the resulting numpy array into a N x M x 3 array
I tried to make the somefunc function return a tuple of rbg values to use (r, b g) but that does not seem to work
It will of course completely depend on what you want to do with the values you supply to the function. So let's assume you just want to put the x values as the red channel and the y values as the blue channel, this could look like
def somefunc(x_value, y_value):
return np.dstack((x_value/5., np.zeros_like(x_value), y_value/5.))
Complete example:
import numpy as np
import matplotlib.pyplot as plt
x_min = 1
x_max = 5
y_min = 1
y_max = 5
Nx = 5 #number of steps for x axis
Ny = 5 #number of steps for y axis
x = np.linspace(x_min, x_max, Nx)
y = np.linspace(y_min, y_max, Ny)
#Can then create a meshgrid using this to get the x and y axis system
xx, yy = np.meshgrid(x, y)
#imagine I have some funcion that does someting based on the x and y values
def somefunc(x_value, y_value):
return np.dstack((x_value/5., np.zeros_like(x_value), y_value/5.))
res = somefunc(xx, yy)
plt.figure(dpi=100)
plt.imshow(res)
plt.show()
If you already have a (more complicated) function that returns an RGB tuple you may loop over the grid to fill an empty array with the values of the function.
#If you already have some function that returns an RGB tuple
def somefunc(x_value, y_value):
if x_value > 2 and y_value < 3:
return np.array(((y_value+1)/4., (y_value+2)/5., 0.43))
elif x_value <=2:
return np.array((y_value/5., (x_value+3)/5., 0.0))
else:
return np.array((x_value/5., (y_value+5)/10., 0.89))
# you may loop over the grid to fill a new array with those values
res = np.zeros((xx.shape[0],xx.shape[1],3))
for i in range(xx.shape[0]):
for j in range(xx.shape[1]):
res[i,j,:] = somefunc(xx[i,j],yy[i,j])
plt.figure(dpi=100)
plt.imshow(res)
I have a question revolving around what would be a viable approach to placing out random-sized squares on a symmetrical, non-visible grid on a tkinter-canvas. I'm going to explain it quite thoroughly as it's a somewhat proprietary problem.
This far I've tried to solve it mostly mathematically. But I've found it to be quite a complex problem, and it seems reasonable that there would be a better approach to take it on than what I've tried.
In its most basic form the code looks like this:
while x_len > canvas_width:
xpos = x_len + margin
squares[i].place(x=xpos, y=ypos)
x_len += square_size + space
i += 1
x_len is the total width of all the squares on a given row, and resets when exiting the while-loop (eg. when x_len > window width), among with xpos (the position on X), as well as altering Y-axis to create a new row.
When placing same-size squares it looks like this:
So far so good.
However when the squares are of random-size it looks like this (at best):
The core problem, beyond that the layout can be quite unpredictable, is that the squares aren't centered to the "invisible grid" - because there is none.
So to solve this I've tried an approach where I use a fixed distance and a relative distance based on every given square. This yields satisficing results for the Y-axis on the first row, but not on the X-axis, nor the following rows on Y.
See example (where first row is centered on Y, but following rows and X is not):
So with this method I'm using a per-square alteration in both Y- and X-axis, based on variables that I fetch from a list that contain widths for all of the generated squares.
In it's entirety it looks like this (though it's work in progress so it's not very well optimized):
square_widths = [60, 75, 75, 45...]
space = square_size*0.5
margin = (square_size+space)/2
xmax = frame_width - margin - square_size
xmin = -1 + margin
def iterate(ypos, xpos, x_len):
y = ypos
x = xpos
z = x_len
i=0
m_ypos = 0
extra_x = 0
while len(squares) <= 100:
n=-1
# row_ypos alters y for every new row
row_ypos += 200-square_widths[n]/2
# this if-statement is not relevant to the question
if x < 0:
n=0
xpos = x
extra_x = x
x_len = z
while x_len < xmax:
ypos = row_ypos
extra_x += 100
ypos = row_ypos + (200-square_widths[n])/2
xpos = extra_x + (200-square_widths[n])/2
squares[i].place(x=xpos, y=ypos)
x_len = extra_x + 200
i += 1
n += 1
What's most relevant here is row_ypos, that alters Y for each row, as well as ypos, that alters Y for each square (I don't have a working calculation for X yet). What I would want to achieve is a similar result that I get for Y-axis on the first row; on all rows and columns (eg. both in X and Y). To create a symmetrical grid with squares of different sizes.
So my questions are:
Is this really best practice to solve this?
If so - Do you have any tips on decent calculations that would do the trick?
If not - How would you approach this?
A sidenote is that it has to be done "manually" and I can not use built-in functions of tkinter to solve it.
Why don't you just use the grid geometry manager?
COLUMNS = 5
ROWS = 5
for i in range(COLUMNS*ROWS):
row, col = divmod(i, COLUMNS)
l = tk.Label(self, text=i, font=('', randint(10,50)))
l.grid(row=row, column=col)
This will line everything up, but the randomness may make the rows and columns different sizes. You can adjust that with the row- and columnconfigure functions:
import tkinter as tk
from random import randint
COLUMNS = 10
ROWS = 5
class GUI(tk.Frame):
def __init__(self, master=None, **kwargs):
tk.Frame.__init__(self, master, **kwargs)
labels = []
for i in range(COLUMNS*ROWS):
row, col = divmod(i, COLUMNS)
l = tk.Label(self, text=i, font=('', randint(10,50)))
l.grid(row=row, column=col)
labels.append(l)
self.update() # draw everything
max_width = max(w.winfo_width() for w in labels)
max_height = max(w.winfo_height() for w in labels)
for column in range(self.grid_size()[0]):
self.columnconfigure(col, minsize=max_width) # set all columns to the max width
for row in range(self.grid_size()[1]):
self.rowconfigure(row, minsize=max_height) # set all rows to the max height
def main():
root = tk.Tk()
win = GUI(root)
win.pack()
root.mainloop()
if __name__ == "__main__":
main()
I found the culprit that made the results not turn out the way expected, and it wasn't due to the calculations. Rather it turned out that the list I created didn't put the squares in correct order (which I should know since before).
And so I fetched the width from the raw data itself, which makes a lot more sense than creating a list.
The function now looks something like this (again, it's still under refinement, but I just wanted to post this, so that people don't waste their time in coming up with solutions to an already solved problem :)):
def iterate(ypos, xpos, x_len):
y = ypos
x = xpos
z = x_len
i=0
while len(squares) <= 100:
n=0
if y > 1:
ypos -= max1 + 10
if y < 0:
if ypos < 0:
ypos=10
else:
ypos += max1 + 10 #+ (max1-min1)/2
if x < 0:
n=0
xc=0
xpos = x
x_len = z
while x_len < xmax:
yc = ypos + (max1-squares[i].winfo_width())/2
if xpos <= 0:
xpos = 10
else:
xpos += max1 + 10
xc = xpos + (max1-squares[i].winfo_width())/2
squares[i].place(x=xc, y=yc)
x_len += max1 + 10
print (x_len)
i += 1
n += 1
I am using np.ogrid to create the x and y grid from which I am drawing my values. I have tried a number of different ways to color the scheme according to the iterations required for |z| >= 2 but nothing seems to work. Even when iterating 10,000 times just to be sure that I have a clear picture when zooming, I cannot figure out how to color the set according to iteration ranges. Here is the code I am using, some of the structure was borrowed from a tutorial. Any suggestions?
#I found this function and searched in numpy for best usage for this type of density plot
x_val, y_val = np.ogrid[-2:2:2000j, -2:2:2000j]
#Creating the values to work with during the iterations
c = x_val + 1j*y_val
z = 0
iter_num = int(input("Please enter the number of iterations:"))
for n in range(iter_num):
z = z**2 + c
if n%10 == 0:
print("Iterations left: ",iter_num - n)
#Creates the mask to filter out values of |z| > 2
z_mask = abs(z) < 2
proper_z_mask = z_mask - 255 #switches current black/white pallette
#Creating the figure and sizing for optimal viewing on a small laptop screen
plt.figure(1, figsize=(8,8))
plt.imshow(z_mask.T, extent=[-2, 2, -2, 2])
plt.gray()
plt.show()