Change linear x-axis to circular x-axis - python

I have a series of numbers from 0 to 360 that I want to plot on the x-axis. The x-axis should be "circular", ie there should be no negative numbers before zero, but 359 instead of -1, 358 instead of -2, etc.
I would like a plot whose x-axis goes from 320 to 40, something like:
https://imgur.com/k1Ss2WJ
I don't want to manually change the data and the ticks on the axes, but I'd like to know if there is a more direct way, keeping the data as it is.

It's pretty simple. You need to use %, known as the modulo operator. This is how you'll convert your x axis numbers:
# Say your numbers are like these:
xaxis = [-1, -2, 600, 200, 360, 0, 6]
mod_xaxis = [x % 360 for x in xaxis]
# mod_xaxis is now [359, 358, 240, 200, 0, 0, 6]

Related

Full graph without calculating all points, possible?

I would like to draw a graph with predefined values from 0 to 605 for example. My pc is not powerful enough to calculate everything so I would like to calculate only some points and connect them all to have a curve on the interval [0;605]. How can I do this? Is this possible?
I tried to put a step, but it automatically reduces the interval.
In my current code it only shows me 605/10 values = 60, so the range of the graph is from 0 to 60 for the x-axis.
tab=[]
for k in range(1,605,10):
img2 = rgb(k)
d = psnr(img1,img2)
tab.append(d)
plt.plot(tab)
plt.xlabel("k")
plt.ylabel("PSNR")
plt.show()
You can set the xticks by yourself: plt.xticks(x_values, [0, 10, 20, 30, ...])
You need to plot with the pattern plt.plot(x, y) instead of only plt.plot(y).
A quick fix (just to show the idea) can be:
Create an empty x, just like tab: x = []
Append k to x in the loop: x.append(k)
Do plt.plot(x, tab), instead of plt.plot(tab)

Values on the X-axis shifting toward to zero

I generated a graph in which the values on the X-axis start from 0 and go to 1000, fifty by fifty, like 0, 50, 100, 150, ..., 900, 950, 1000. However, I want to divide the values on the X-axis by 10 (I want to convert the values on the x-axis into 0, 5, 10, 15, ..., 90, 95, 100).
Index_time is 1001
index_time = len(df.index)
ax.plot(np.arange(index_time), df["SoluteHBonds"], color="blue")
ranges=(np.arange(0,index_time,50))
ax.set_xticks(ranges)
When I divide the values on the X-axis via np.true_divide(ranges, 10), all the values on the X-axis shift toward 0
On the other hand, I tried to create a list first and then divide each element by 10 but the result is still the same.
lst_range=list(range(0,int((index_time-1)/10),5))
ax.set_xticks([time/10 for time in lst_range])
What could be the problem or what is the thing that I am missing in this case?
Thanks in advance!

Extracting data from a histogram with custom bins in Python

I have a data set of distances between two particles, and I want to bin these data in custom bins. For example, I want to see how many distance values lay in the interval from 1 to 2 micrometers, and so on. I wrote a code about it, and it seems to work. This is my code for this part:
#Custom binning of data
bins= [0,1,2,3,4,5,6,7,8,9,10]
fig, ax = plt.subplots(n,m,figsize = (30,10)) #using this because I actually have 5 histograms, but only posted one here
ax.hist(dist_from_spacer1, bins=bins, edgecolor="k")
ax.set_xlabel('Distance from spacer 1 [µm]')
ax.set_ylabel('counts')
plt.xticks(bins)
plt.show()
However, now I wish to extract those data values from the intervals, and store them into lists. I tried to use:
np.histogram(dist_from_spacer1, bins=bins)
However, this just gives how many data points are on each bin and the bin intervals, just like this:
(array([ 0, 0, 44, 567, 481, 279, 309, 202, 117, 0]),
array([ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]))
How can I get the exact data that belong to each histogram bin?
Yes, np.histogram calculates what you need for a histogram, and hence the specific data points are not necessary, just bins' boundaries and count for each bin. However, the bins' boundaries is sufficient to acheive what you want by using np.digitizr
counts, bins = np.histogram(dist_from_spacer1)
indices = np.digitize(dist_from_spacer1, bins)
lists = [[] for _ in range(len(bins))]
[lists[i].append(x) for i, x in zip(indices, dist_from_spacer1)
In your case, the bins' boundaries are predefined, so you can use np.digitize directly

How to encircle some pixels on a heat map with a continuous, not branched line using Python?

I am using plt.imshow() to plot values on a grid (CCD data in my case). An example plot:
I need to indicate a barrier on it, to show which pixels I care about. This is similar to what I need:
I know how to add squares to an image, gridlines to an image, but this knowledge doesn't solve the issuue, nor adding single squares to the pic, which is also within my abilities. I need a line which encircles an area on the grid (and this line will always need to go between pixels, not across them so this might make it simpler a bit).
How can I do this?
Iury Sousa has provided a nice work-around to the question above. However, it is not strictly circling the area with a line (rather plotting a mask to the picture and then covering most of it with the picture again), and it fails when I try to encircle overlapping group of pixels. ImportanceOfBeingErnest suggested in the comments that I should simply use the plt.plot sample. Using Iury Sousa's example as a starting point lets have:
X,Y = np.meshgrid(range(30),range(30))
Z = np.sin(X)+np.sin(Y)
selected1 = Z>1.5
Now selected1 is an array of boolean arrays, and we would like to circle only those pixels which have corresponding Z value above 1.5. We also would like to circle selected2, which contains True values for pixels with value above 0.2 and below 1.8:
upperlim_selected2 = Z<1.8
selected2 = upperlim_selected2>0.2
Iury Sousa's great work-around doesn't work for this case. plt.plot would, in my opinion. What is an efficient way to achieve the circling of selected1 and selected2, either using plt.plot or another method?
I tried something that should fit your needs.
First of all, I defined an arbitrary data:
X,Y = np.meshgrid(range(30),range(30))
Z = np.sin(X)+np.sin(Y)
Here you can define the condition which fits in the pattern you want to highlight:
selected = Z>1.5
To plot you will use scatter instead of imshow. You will plot all the data, then the selected data two more times, one with larger squares with the highlight color and another normally using the same color reference and limits.
info = dict(marker='s',vmin=-2,vmax=2)
fig,ax = plt.subplots()
plt.scatter(X.ravel(),Y.ravel(),100,c=Z.ravel(),**info)
plt.scatter(X[selected].ravel(),Y[selected].ravel(),150,c='r',marker='s')
plt.scatter(X[selected].ravel(),Y[selected].ravel(),100,c=Z[selected].ravel(),**info)
ax.axis('equal')
Similar to the answer in Can matplotlib contours match pixel edges?
you can create a grid with a higher resolution and draw a contour plot.
import numpy as np
import matplotlib.pyplot as plt
X,Y = np.meshgrid(range(30),range(30))
Z = np.sin(X)+np.sin(Y)
resolution = 25
f = lambda x,y: Z[int(y),int(x) ]
g = np.vectorize(f)
x = np.linspace(0,Z.shape[1], Z.shape[1]*resolution)
y = np.linspace(0,Z.shape[0], Z.shape[0]*resolution)
X2, Y2= np.meshgrid(x[:-1],y[:-1])
Z2 = g(X2,Y2)
plt.pcolormesh(X,Y, Z)
plt.contour(X2,Y2,Z2, [1.5], colors='r', linewidths=[1])
plt.show()
Another solution which works for me:
Lets have a grid for example:
grid=[[0, 6, 8, 2, 2, 5, 25, 24, 11],
[4, 15, 3, 22, 225, 1326, 2814, 1115, 18],
[6, 10, 9, 201, 3226, 3549, 3550, 3456, 181],
[42, 24, 46, 1104, 3551, 3551, 3551, 3382, 27],
[9, 7, 73, 2183, 3551, 3551, 3551, 3294, 83],
[9, 7, 5, 669, 3544, 3551, 3074, 1962, 18],
[10, 3545, 9, 10, 514, 625, 16, 14, 5],
[5, 6, 128, 10, 8, 6, 7, 40, 4]]
We plot it:
plt.pcolormesh(grid)
Assume we want to encircle every pixel with value higher than 1420. We create a boolean array:
threshold=1420
booleangrid=np.asarray(grid)>threshold
intgrid=booleangrid*1
We then create a line segment around every pixel:
down=[];up=[];left=[];right=[]
for i, eachline in enumerate(intgrid):
for j, each in enumerate(eachline):
if each==1:
down.append([[j,j+1],[i,i]])
up.append([[j,j+1],[i+1,i+1]])
left.append([[j,j],[i,i+1]])
right.append([[j+1,j+1],[i,i+1]])
and join them together:
together=[]
for each in down: together.append(each)
for each in up: together.append(each)
for each in left: together.append(each)
for each in right: together.append(each)
(Creted separately for clarity.)
We go through these individual line segments, ant keep only those which appear only once, ie the ones on the edge of the feature defined by the boolean array (booleangrid) we defined above:
filtered=[]
for each in together:
c=0
for EACH in together:
if each==EACH:
c+=1
if c==1:
filtered.append(each)
Then we plot the grid and idividual line segments with a for loop:
plt.pcolormesh(grid)
for x in range(len(filtered)):
plt.plot(filtered[x][0],filtered[x][1],c='red', linewidth=8)
giving us the result:
With which we can be happy with.

The bin count displayed by pyplot hist doesn't match the actual counts?

I have a list of ints--I call it 'hours1'--ranging from 0-23. Now this list is for 'hours' of a day in a 24 hour clock. I, however, want to transform it to a different time zone (move up 7 hours). This is simple enough, and I do it so that now I have 2 lists: hours1 and hours2.
I use the following code to plot a histogram:
bins = range(24)
plt.hist(hours,bins=bins, normed=0, facecolor='red', alpha=0.5)
plt.axis([0, 23, 0, 1000])
it works perfectly for hours1. For hours2 the last value (that of the bin for 23s) is too high. This is not a counting error/transformation error because when I count my hours2 list, I get 604 23s, which matches the what I expect (having 604 16s in hours1).
so this is a very long winded way of saying, the height of the bins do not match what I get when I count the data myself...
The issue was a binning one. In short, I wasn't paying attention/thinking about what I wanted to display. More specifically this was the correct code:
bins = range(25)
plt.hist(hours, normed=0, facecolor='green', alpha=0.5, bins=bins)
plt.axis([0, 24, 0, 1500])
that is, there are 23 hours in a day, which means 24 seperate 'hour bins' counting 0. but the correct edge values for this are bins = range(25) (so that 23 gets placed in 23-24) and the correct axis is 0 to 24, (so bin 23 has width of 1). simple mistake, but i guess we've all bin there and done that?

Categories