Add Datalabel on the end of barchart (lollypop) - python

I'm totally new at using Python for Power BI (or anything really).
I would like to add the value of the bar/scatter at the end of the line. (the datalabel)
Also to have a version where I could have the label inside of the scatter bubble would be cool.
Anyone who could help out here ?
All help appreciated
# libraries
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
# Create a dataframe
df = pd.DataFrame({'group': dataset.Genre , 'values': dataset.Revenue})
val = list(dataset.SelectedGenre)
# Reorder it following the values:
ordered_df = df.sort_values(by='values')
my_range=range(1,len(df.index)+1)
# Create a color if the group is "B"
my_color=np.where(ordered_df ['group']== val, 'orange', 'skyblue')
my_size=np.where(ordered_df ['group']== val , 150, 150)
# The vertival plot is made using the hline function
# I load the seaborn library only to benefit the nice looking feature
import seaborn as sns
val = ordered_df['values']
plt.hlines(y=my_range, xmin=0, xmax=val, color=my_color, alpha=1 , linewidth=8)
plt.scatter(val, my_range, color=my_color, s=my_size, alpha=1)
# Add title and axis names
plt.yticks(my_range, ordered_df['group'])
plt.title("What about the B group?", loc='left')
plt.xlabel('Value of the variable')
plt.ylabel('Group')
plt.box(False) #Turn of Black bx around visual
plt.show()

Found it myself
import matplotlib.pyplot as plt
import numpy as np
# Data
x = dataset.Revenue
y = dataset.Genre
labels = dataset.Revenue
val = list(dataset.SelectedGenre)
# Create the figure and axes objects
fig, ax = plt.subplots(1, figsize=(10, 6))
fig.suptitle('Example Of Labelled Scatterpoints')
my_color=np.where(y == val, 'orange', 'skyblue')
my_size=np.where( y == val , 2000, 2000)
# Plot the scatter points
ax.scatter(x, y,
color= my_color, # Color of the dots
s=1000, # Size of the dots
alpha=1, # Alpha of the dots
linewidths=1) # Size of edge around the dots
ax.hlines(y, xmin=0, xmax=x, color= my_color, alpha=1 , linewidth=8)
def human_format(num):
magnitude = 0
while abs(num) >= 1000:
magnitude += 1
num /= 1000
# add more suffixes if you need them
return '%.0f%s' % (round(num), ['', 'K', 'M', 'G', 'T', 'P'][magnitude])
# Add the participant names as text labels for each point
for x_pos, y_pos, label in zip(x, y, labels):
ax.annotate(
human_format(label), # The label for this point
xy=(x_pos, y_pos), # Position of the corresponding point
xytext=(-8, 0), # Offset text by 7 points to the right
textcoords='offset points', # tell it to use offset points
ha='left', # Horizontally aligned to the left
va='center',
color = 'white') # Vertical alignment is centered
plt.box(False) #Turn of Black bx around visual
# Show the plot
plt.show()

Related

matplotlib: reduce empty space on axis

The following code (obtained from here):
import matplotlib.pyplot as plt
import pandas as pd
# Prepare Data
df = pd.read_csv("https://github.com/selva86/datasets/raw/master/mtcars.csv")
x = df.loc[:, ['mpg']]
df['mpg_z'] = (x - x.mean())/x.std()
df['colors'] = ['red' if x < 0 else 'darkgreen' for x in df['mpg_z']]
df.sort_values('mpg_z', inplace=True)
df.reset_index(inplace=True)
# Draw plot
plt.figure(figsize=(14,16), dpi= 80)
plt.scatter(df.mpg_z, df.index, s=450, alpha=.6, color=df.colors)
for x, y, tex in zip(df.mpg_z, df.index, df.mpg_z):
t = plt.text(x, y, round(tex, 1), horizontalalignment='center',
verticalalignment='center', fontdict={'color':'white'})
# Decorations
# Lighten borders
plt.gca().spines["top"].set_alpha(.3)
plt.gca().spines["bottom"].set_alpha(.3)
plt.gca().spines["right"].set_alpha(.3)
plt.gca().spines["left"].set_alpha(.3)
plt.yticks(df.index, df.cars)
plt.title('Diverging Dotplot of Car Mileage', fontdict={'size':20})
plt.xlabel('$Mileage$')
plt.grid(linestyle='--', alpha=0.5)
plt.xlim(-2.5, 2.5)
plt.show()
Gives me this:
What I'm trying to do is reduce the empty space on the y-axis, indicated here by the red bars:
How can I do this? Changing the height of the figure doesn't seem to help.
One quick solution is to manually set the margins using
plt.margins(y=0) # no margin at all
plt.margins(y=1/len(df)) # equal margin

Data value of a variable in each marker within subplots

I'm developing the following function: extract_name_value() that generates a step chart taking the values of a pandas DataFrame in Python, for now it works fine, but I want to add the values of the variable points_axisyvalue or values_list to it in each marker: Script Here
I tried to use the following examples:Data value at each marker, Matplotlib scatter plot with different text at each data point or How to put individual tags for a matplotlib scatter plot?, which would be something like what I want; also I even tried using plt.annotate(), but the data of the values does not come out the way I want it, plus I think it would cover up the graph a lot and not appreciate well. Below I put the code in which I'm using plt.annotate():
# Function to extract the Name and Value attributes
def extract_name_value(signals_df, rootXML):
# print(signals_df)
names_list = [name for name in signals_df['Name'].unique()]
num_names_list = len(names_list)
num_axisx = len(signals_df["Name"])
values_list = [value for pos, value in enumerate(signals_df["Value"])]
print(values_list)
points_axisy = signals_df["Value"]
print(len(points_axisy))
colors = ['b', 'g', 'r', 'c', 'm', 'y']
# Creation Graphic
fig, ax = plt.subplots(nrows=num_names_list, figsize=(20, 30), sharex=True)
plt.suptitle(f'File XML: {rootXML}', fontsize=16, fontweight='bold', color='SteelBlue', position=(0.75, 0.95))
plt.xticks(np.arange(-1, num_axisx), color='SteelBlue', fontweight='bold')
labels = ['value: {0}'.format(j) for j in values_list]
print(labels)
i = 1
for pos, name in enumerate(names_list):
# get data
data = signals_df[signals_df["Name"] == name]["Value"]
print(data)
# get color
j = random.randint(0, len(colors) - 1)
# get plots by index = pos
x = np.hstack([-1, data.index.values, len(signals_df) - 1])
y = np.hstack([0, data.values, data.iloc[-1]])
ax[pos].plot(x, y, drawstyle='steps-post', marker='o', color=colors[j], linewidth=3)
ax[pos].set_ylabel(name, fontsize=8, fontweight='bold', color='SteelBlue', rotation=30, labelpad=35)
ax[pos].yaxis.set_major_formatter(ticker.FormatStrFormatter('%0.1f'))
ax[pos].yaxis.set_tick_params(labelsize=6)
ax[pos].grid(alpha=0.4)
i += 1
for label, x, y in zip(labels, x, y):
plt.annotate(label, xy=(x, y), xytext=(-20, 20), textcoords='offset points', ha='right', va='bottom', bbox=dict(boxstyle='round,pad=0.5', fc='yellow', alpha=0.5),
arrowprops=dict(arrowstyle='->', connectionstyle='arc3,rad=0'))
plt.show()
What I get is the annotations spliced and in different positions.
But, What does my code need to show each value at each point?
I've also been trying to use the code from the Matplotlib reference and couldn't get it done: Marker Reference. Thank you very much in advance, any comment helps.
You can use plt.annotate function in a loop to solve your problem.
I randomly generated some data and plotted it as a single plot. You can do the same inside a subplot, the function would be the same.
# sample data points for the plot
x=np.arange(1,10)
y=np.linspace(20,40,9)
plt.figure(figsize=[15,5],dpi=200)
plt.plot(x,y,drawstyle='steps-post', marker='o')
# using annotate function to show the changepoints in a loop
for i in range(len(x)):
# I rounded the y values as string and used the same x and y coords as the locations
# next we can give a constant offset points to offset the annotation from each value
# here I used (-20,20) as the offset values
plt.annotate(f"{str(round((y[i])))}",(x[i],y[i]),xycoords='data',
xytext=(-20,20), textcoords='offset points',color="r",fontsize=12,
arrowprops=dict(arrowstyle="->", color='black'))
You can remove the arrowprops if you don't want the arrows.
Edited
I used the example1.xml file in your GitHub repo and edited the function a bit. All I did was add a loop and an if-else condition to your function.
# Initial part is same as yours
names_list = [name for name in signals_df['Name'].unique()]
num_names_list = len(names_list)
num_axisx = len(signals_df["Name"])
values_list = [value for pos, value in enumerate(signals_df["Value"])]
points_axisy = signals_df["Value"]
colors = ['b', 'g', 'r', 'c', 'm', 'y']
# start new figure
plt.figure(figsize=[20,28],dpi=200)
#start a loop with the subplots
for i in range(len(names_list)):
# subplot has 14 rows, 1 column and the i+1 represents the i'th plot
plt.subplot(num_names_list,1,i+1)
# choose color
col=np.random.randint(0, len(colors) - 1)
# get the locations of the values with the similar name in your list
locs=signals_df['Name']==names_list[i]
# get the values in those locations
data=signals_df['Value'][locs]
# arrange the x and y coordinates
x = np.hstack([-1, data.index.values, len(signals_df) - 1])
y = np.hstack([0, data.values, data.iloc[-1]])
# plot the values as usual
plt.plot(x, y, drawstyle='steps-post', marker='o', color=colors[col], linewidth=3)
plt.ylabel(names_list[i], fontsize=8, fontweight='bold', color='SteelBlue', rotation=30, labelpad=35)
plt.grid(alpha=0.4)
# this loop is for annotating the values
for j in range(len(x)):
# I found it is better to alternate the position of the annotations
# so that they wont overlap for the adjacent values
if j%2==0:
# In this condition the xytext position is (-20,20)
# this posts the annotation box over the plot value
plt.annotate(f"Val={round((y[j]))}",(x[j],y[j]),xycoords='data',
xytext=(-20,20), textcoords='offset points',color="r",fontsize=8,
arrowprops=dict(arrowstyle="->", color='black'),
bbox=dict(boxstyle='round', pad=0.5, fc='yellow', alpha=0.5))
else:
# In this condition the xytext position is (-20,-20)
# this posts the annotation box under the plot value
plt.annotate(f"Val={round((y[j]))}",(x[j],y[j]),xycoords='data',
xytext=(-20,-20), textcoords='offset points',color="r",fontsize=8,
arrowprops=dict(arrowstyle="->", color='black'),
bbox=dict(boxstyle='round', pad=0.5, fc='yellow', alpha=0.5))
New Function Result
I hope that it is useful.
I think it should be quite close to what you are after. I randomly generate data, then annotate it using matplotlib.text. It's not very pretty, you might want to add some padding and more refinements, but I hope it gives a good idea!
If two points are too close, you might want to annotate one on the left, and the other one on the right, like I am doing for the first point. I have not seen such a situation in the examples that you have given, so it's not handled.
Function place_label(label, xy, position, ax, pad=0.01) places the label where you want it to be. The rest of the code is demonstrating that it works, using randomly generated data.
import random
import numpy as np
import matplotlib.pyplot as plt
# function that places the label give the desired position
def place_label(label, xy, position, ax, pad=0.01):
# annotate in the initial position, xy is the top right corner of the bounding box
t_ = ax.text(x=xy[0], y=xy[1], s=label, fontsize=16)
# find useful values
tbb = t_.get_window_extent(renderer=rend)
abb = ax.get_window_extent(renderer=rend)
a_xlim, a_ylim = ax.get_xlim(), a_.get_ylim()
# now adjust the position if needed
new_xy = [xy[0], xy[1]]
relative_width = tbb.width/abb.width * (a_xlim[1] - a_xlim[0])
pad_x = pad * (a_xlim[1] - a_xlim[0])
assert(position[0] in ['l', 'c', 'r'])
if position[0] == 'c':
new_xy[0] -= relative_width/2
elif position[0] == 'l':
new_xy[0] -= relative_width + pad_x
else:
new_xy[0] += pad_x
relative_height = tbb.height/abb.height * (a_ylim[1] - a_ylim[0])
pad_y = pad * (a_ylim[1] - a_ylim[0])
assert(position[1] in ['b', 'c', 't'])
if position[1] == 'c':
new_xy[1] -= relative_height/2
elif position[1] == 'b':
new_xy[1] -= relative_height + pad_y
else:
new_xy[1] += pad_y
t_.set_position(new_xy)
return t_
# generate data, plot it and annotate it!
axes_qty = 9
axes_gap = 0.035
fig = plt.figure(figsize=(10, 8))
ax = [plt.axes([axes_gap, axes_gap/2 + i*(1/axes_qty), 1-2*axes_gap, 1/axes_qty-axes_gap]) for i in range(axes_qty)]
rend = fig.canvas.get_renderer()
for a_ in ax:
x_ = [random.randint(0, 10) for _ in range(5)]
x_ = np.unique(x_)
y_ = [random.randint(0, 12) for _ in x_]
# as x is shared, we set the limits in advance, otherwise the adjustments won't be accurate
a_.set_xlim([-0.5, 10.5])
# plotting the data
data_ = [[x_[0], y_[0]]]
for i in range(1, len(x_)):
data_ += [[x_[i-1], y_[i]], [x_[i], y_[i]]]
a_.plot([d[0] for d in data_], [d[1] for d in data_])
mid_y = 0.5 * (a_.get_ylim()[0] + a_.get_ylim()[1])
# now let's label it
for i in range(len(x_)):
# decide what point we annotate
if i == 0:
xy = [x_ [0], y_[0]]
else:
xy = [x_[i-1], y_[i]]
# decide its position
position_0 = 'l' if i == 0 else 'r'
position_1 = 'b' if xy[1] > mid_y else 't'
place_label(label=str(xy[1]), xy=xy, position=position_0+position_1, ax=a_)
plt.show()

Python matplotlib polar coordinate is not plotting as it is supposed to be

I am plotting from a CSV file that contains Cartesian coordinates and I want to change it to Polar coordinates, then plot using the Polar coordinates.
Here is the code
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import seaborn as sns
df = pd.read_csv('test_for_plotting.csv',index_col = 0)
x_temp = df['x'].values
y_temp = df['y'].values
df['radius'] = np.sqrt( np.power(x_temp,2) + np.power(y_temp,2) )
df['theta'] = np.arctan2(y_temp,x_temp)
df['degrees'] = np.degrees(df['theta'].values)
df['radians'] = np.radians(df['degrees'].values)
ax = plt.axes(polar = True)
ax.set_aspect('equal')
ax.axis("off")
sns.set(rc={'axes.facecolor':'white', 'figure.facecolor':'white','figure.figsize':(10,10)})
# sns.scatterplot(data = df, x = 'x',y = 'y', s= 1,alpha = 0.1, color = 'black',ax = ax)
sns.scatterplot(data = df, x = 'radians',y = 'radius', s= 1,alpha = 0.1, color = 'black',ax = ax)
plt.tight_layout()
plt.show()
Here is the dataset
If you run this command using polar = False and use this line to plot sns.scatterplot(data = df, x = 'x',y = 'y', s= 1,alpha = 0.1, color = 'black',ax = ax) it will result in this picture
now after setting polar = True and run this line to plot sns.scatterplot(data = df, x = 'radians',y = 'radius', s= 1,alpha = 0.1, color = 'black',ax = ax) It is supposed to give you this
But it is not working as if you run the actual code the shape in the Polar format is the same as Cartesian which does not make sense and it does not match the picture I showed you for polar (If you are wondering where did I get the second picture from, I plotted it using R)
I would appreciate your help and insights and thanks in advance!
For a polar plot, the "x-axis" represents the angle in radians. So, you need to switch x and y, and convert the angles to radians (I also added ax=ax, as the axes was created explicitly):
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import seaborn as sns
data = {'radius': [0, 0.5, 1, 1.5, 2, 2.5], 'degrees': [0, 25, 75, 155, 245, 335]}
df_temp = pd.DataFrame(data)
ax = plt.axes(polar=True)
sns.scatterplot(x=np.radians(df_temp['degrees']), y=df_temp['radius'].to_numpy(),
s=100, alpha=1, color='black', ax=ax)
for deg, y in zip(df_temp['degrees'], df_temp['radius']):
x = np.radians(deg)
ax.axvline(x, color='skyblue', ls=':')
ax.text(x, y, f' {deg}', color='crimson')
ax.set_rlabel_position(-15) # Move radial labels away from plotted dots
plt.tight_layout()
plt.show()
About your new question: if you have an xy plot, and you convert these xy values to polar coordinates, and then plot these on a polar plot, you'll get again the same plot.
After some more testing with the data, I decided to create the plot directly with matplotlib, as seaborn makes some changes that don't have exactly equal effects across seaborn and matplotlib versions.
What seems to be happening in R:
The angles (given by "x") are spread out to fill the range (0,2 pi). This either requires a rescaling of x, or change how the x-values are mapped to angles. One way to get this, is subtracting the minimum. And with that result divide by the new maximum and multiply by 2 pi.
The 0 of the angles it at the top, and the angles go clockwise.
The following code should create the plot with Python. You might want to experiment with alpha and with s in the scatter plot options. (Default the scatter dots get an outline, which often isn't desired when working with very small dots, and can be removed by lw=0.)
ax = plt.axes(polar=True)
ax.set_aspect('equal')
ax.axis('off')
x_temp = df['x'].to_numpy()
y_temp = df['y'].to_numpy()
x_temp -= x_temp.min()
x_temp = x_temp / x_temp.max() * 2 * np.pi
ax.scatter(x=x_temp, y=y_temp, s=0.05, alpha=1, color='black', lw=0)
ax.set_rlim(y_temp.min(), y_temp.max())
ax.set_theta_zero_location("N") # set zero at the north (top)
ax.set_theta_direction(-1) # go clockwise
plt.show()
At the left the resulting image, at the right using the y-values for coloring (ax.scatter(..., c=y_temp, s=0.05, alpha=1, cmap='plasma_r', lw=0)):

Matplotlib legend makes the image too large

I'm plotting this figure with matplotlib, the for loop just color the background:
fig, ax = plt.subplots()
ax.set_ylabel('Number of contacts')
ax.set_xlabel('Time [s]')
for m in range(len(data[node])):
if data[node][m] == -1:
ax.axvline(m,color='r',linewidth=5,alpha=0.2,label="OUT")
if data[node][m] == 0:
ax.axvline(m,color='g',linewidth=5,alpha=0.2,label="RZ0")
if data[node][m] == 1:
ax.axvline(m,color='y',linewidth=5,alpha=0.2,label="RZ1")
ax.plot(x, y, 'b+')
# ax.legend() # HERE is the problem
plt.show()
Which plots the following:
What I want now is a legend to indicate each color of the background meaning, but when I include ax.legend() I get the following error:
ValueError: Image size of 392x648007 pixels is too large. It must be less than 2^16 in each
direction.
<Figure size 432x288 with 1 Axes>
<Figure size 432x288 with 0 Axes>
How am I supposed to name each color of the background, there are 43200 vertical lines but only 3 colors, does it have anything to do with the number of lines?
The trick is to set the label only once. You can add a variable for each label and replace it with None once it's used. Note that using axvline to draw a background has the problem that the line width is measured in pixel space, so neighboring lines will either overlap or have a small white space inbetween. Better to use axvspan. To avoid the white space at the left and at the right, you can explicitly set the x-limits.
The code can be simplified somewhat using a loop.
Updated code:
group consecutive spans together for drawing
precalculate the effect of alpha so the background can be drawn without the need for transparency
from matplotlib import pyplot as plt
from matplotlib import colors as mcolors
import numpy as np
import pandas as pd
import itertools
fig, ax = plt.subplots()
# create some random data
x = np.arange(100)
y = np.sinh(x/20)
indicators = [-1, 0, 1]
node = 0
data = [np.random.choice(indicators, len(x), p=[10/16,1/16,5/16])]
labels = ["OUT", "RZ0", "RZ1"]
colors = ['lime', 'purple', 'gold']
alpha = 0.4
# precalculate the effect of alpha so the colors can be applied with alpha=1
colors = [[1 + (x - 1) * alpha for x in mcolors.to_rgb(c)] for c in colors]
m = 0
for val, group in itertools.groupby(data[node]):
width = len(list(group))
ind = indicators.index(val)
ax.axvspan(m, m + width, color=colors[ind], linewidth=0, alpha=1, label=labels[ind])
labels[ind] = None # reset the label to make sure it is only used once
m += width
ax.plot(x, y, 'b+')
ax.set_xlim(0, len(data[node]))
ax.legend(framealpha=1) # to make the legend background opaque
plt.show()
Do something like hrz1 = ax.axvline(m,color='y',linewidth=5,alpha=0.2) for each of your classes, and then ax.legend((hrz1, hrz0, hout), ('RZ1', 'RZ0', 'OUT'). The hrz1 pointer will be rewritten for each line you make, and then legend will only make one label for each of the handles.

Not getting the heatmap in the background using Matplotlib Python

I have tried this and got the result as in the image:
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.colors import LinearSegmentedColormap
cmap = LinearSegmentedColormap.from_list("", ["red","grey","green"])
df = pd.read_csv('t.csv', header=0)
fig = plt.figure()
ax1 = fig.add_subplot(111)
ax = ax1.twiny()
# Scatter plot of positive points, coloured blue (C0)
ax.scatter(np.argwhere(df['real'] > 0), df.loc[df['real'] > 0, 'real'], color='C2')
# Scatter plot of negative points, coloured red (C3)
ax.scatter(np.argwhere(df['real'] < 0), df.loc[df['real'] < 0, 'real'], color='C3')
# Scatter neutral values in grey (C7)
ax.scatter(np.argwhere(df['real'] == 0), df.loc[df['real'] == 0, 'real'], color='C7')
ax.set_ylim([df['real'].min(), df['real'].max()])
index = len(df.index)
ymin = df['prediction'].min()
ymax= df['prediction'].max()
ax1.imshow([np.arange(index),df['prediction']],cmap=cmap,
extent=(0,index-1,ymin, ymax), alpha=0.8)
plt.show()
Image:
I was expecting one output where the color is placed according to the figure. I am getting green color and no reds or greys.
I want to get the image or contours spread as the values are. How I can do that? See the following image, something similar:
Please let me know how I can achieve this. The data I used is here: t.csv
For a live version, have a look at Tensorflow Playground
There are essentially 2 tasks required in a solution like this:
Plot the heatmap as the background;
Plot the scatter data;
Output:
Source code:
import numpy as np
import matplotlib.pyplot as plt
###
# Plot heatmap in the background
###
# Setting up input values
x = np.arange(-6.0, 6.0, 0.1)
y = np.arange(-6.0, 6.0, 0.1)
X, Y = np.meshgrid(x, y)
# plot heatmap colorspace in the background
fig, ax = plt.subplots(nrows=1)
im = ax.imshow(X, cmap=plt.cm.get_cmap('RdBu'), extent=(-6, 6, -6, 6), interpolation='bilinear')
cax = fig.add_axes([0.21, 0.95, 0.6, 0.03]) # [left, bottom, width, height]
fig.colorbar(im, cax=cax, orientation='horizontal') # add colorbar at the top
###
# Plot data as scatter
###
# generate the points
num_samples = 150
theta = np.linspace(0, 2 * np.pi, num_samples)
# generate inner points
circle_r = 2
r = circle_r * np.random.rand(num_samples)
inner_x, inner_y = r * np.cos(theta), r * np.sin(theta)
# generate outter points
circle_r = 4
r = circle_r + np.random.rand(num_samples)
outter_x, outter_y = r * np.cos(theta), r * np.sin(theta)
# plot data
ax.scatter(inner_x, inner_y, s=30, marker='o', color='royalblue', edgecolors='white', linewidths=0.8)
ax.scatter(outter_x, outter_y, s=30, marker='o', color='crimson', edgecolors='white', linewidths=0.8)
ax.set_ylim([-6,6])
ax.set_xlim([-6,6])
plt.show()
To keep things simple, I kept the colorbar range (-6, 6) to match the data range.
I'm sure this code can be changed to suit your specific needs. Good luck!
Here is a possible solution.
A few notes and questions:
What are the 'prediction' values in your data file? They do not seem to correlate with the values in the 'real' column.
Why do you create a second axis? What is represented on the bottom X-axis in your plot? I removed the second axis and labelled the remaining axes (index and real).
When you slice a pandas DataFrame, the index comes with it. You don't need to create a separate index (argwhere and arange(index) in your code). I simplified the first part of the code, where scatterplots are produced.
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.colors import LinearSegmentedColormap
cmap = LinearSegmentedColormap.from_list("", ["red","grey","green"])
df = pd.read_csv('t.csv', header=0)
print(df)
fig = plt.figure()
ax = fig.add_subplot(111)
# Data limits
xmin = 0
xmax = df.shape[0]
ymin = df['real'].min()
ymax = df['real'].max()
# Scatter plots
gt0 = df.loc[df['real'] > 0, 'real']
lt0 = df.loc[df['real'] < 0, 'real']
eq0 = df.loc[df['real'] == 0, 'real']
ax.scatter(gt0.index, gt0.values, edgecolor='white', color='C2')
ax.scatter(lt0.index, lt0.values, edgecolor='white', color='C3')
ax.scatter(eq0.index, eq0.values, edgecolor='white', color='C7')
ax.set_ylim((ymin, ymax))
ax.set_xlabel('index')
ax.set_ylabel('real')
# We want 0 to be in the middle of the colourbar,
# because gray is defined as df['real'] == 0
if abs(ymax) > abs(ymin):
lim = abs(ymax)
else:
lim = abs(ymin)
# Create a gradient that runs from -lim to lim in N number of steps,
# where N is the number of colour steps in the cmap.
grad = np.arange(-lim, lim, 2*lim/cmap.N)
# Arrays plotted with imshow must be 2D arrays. In this case it will be
# 1 pixel wide and N pixels tall. Set the aspect ratio to auto so that
# each pixel is stretched out to the full width of the frame.
grad = np.expand_dims(grad, axis=1)
im = ax.imshow(grad, cmap=cmap, aspect='auto', alpha=1, origin='bottom',
extent=(xmin, xmax, -lim, lim))
fig.colorbar(im, label='real')
plt.show()
This gives the following result:

Categories

Resources