Format Data Label as Percentage and Move it next to the other Data Label - label

I'm new here and also a beginner Python user. In this chart I was trying to make, I just need to format the blue labels into percentage with 2 decimal places and move them right next to the values at the end of the bars.
Here is the code:
index = np.arange(6)
fig, ax = plt.subplots(figsize=(10, 2))
list1x = list(dict(train['Outlet_Location_Type'].value_counts()).keys())
list1y = list(train['Outlet_Location_Type'].value_counts())
ax.barh(list1x, list1y, alpha=0.7,
# width = 0.5,
color=cm.Blues([i / 0.00525 for i in [ 0.00808, 0.0045, 0.00281]])
)
plt.rcParams.update({'font.size': 10})
rects = ax.patches
for i, label in enumerate(ii / 8325 * 100 for ii in list1y):
ax.text(label, i, str(label), size=10, ha='left', va='center', color = "blue")
for h, label in enumerate(list1y):
ax.text(label, h, label, size=10, ha='left', va='center')
ax.text(0, 1.02, 'Outlet Location Type Count', transform=ax.transAxes, size=12, weight=600, color='#777777')
ax.xaxis.set_ticks_position('bottom')
ax.tick_params(axis='x', colors='black', labelsize=9)
ax.set_axisbelow(True)
plt.xticks([])
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
plt.show()
I think there could be data type issue causing the blue values to stay at the left side. I can't find the formatting solution to change them into percentage.
Thank you so much in advance!

Related

Fix aspect ratio of a scatter plot with an image

I've to plot multiple scatter and table in a grid space and I'm having a couple of issues with the relative position but most important with defining and maintaining the aspect ratio of the scatter plot.
I've written a script with "fake" data on it to describe my problem and a minimum "not working" example below.
What I have is a dataframe with x, and y positions of objects, and what I want to do is to put the corresponding image below.
Since the image can have an arbitrary aspect ratio I need to read the aspect ratio and construct the scatter plot in that way but I'm unable to make it work.
Another problem is connected with the invert_xaxis and invert_yaxis that don't work (I need that command since the scatter data are inverted.
I've used the following commands, and as far as I've understood each of them should block the aspect ratio of the scatter plot to the same ratio of the figure but they do not work.
The aspect ratio becomes corrected only when the figure is plotted but that eliminates the effect of axis inversion.
I've had a similar problem with setting the aspect ratio of plots without the addition of a figure, sometimes it worked but not with tight_layout.
It is obvious that I'm missing something important....but I'm unable to figure it out.
This is the fake data code:
###############################################################################
# fake data
#general data aspect ratio
image_height= 5 #4270
image_width = 10 # 8192
pix2scale = 0.3125
data_AR = image_height / image_width
#random data generation
data_width = image_width* pix2scale
data_height = image_height * pix2scale
data1x = np.random.uniform(-data_width/2, data_width/2, size=(40))
data1y = np.random.uniform(-data_height/2, data_height/2, size=(40))
data2x = np.random.uniform(-data_width/2, data_width/2, size=(40))
data2y = np.random.uniform(-data_height/2,data_height/2, size=(40))
temp_df1 = pd.DataFrame([data1x,data1y,['random1']*40],index = ['x','y','label']).T
temp_df2 = pd.DataFrame([data2x,data2y,['random2']*40],index = ['x','y','label']).T
df = pd.concat([temp_df1,temp_df2],axis = 0, ignore_index = True)
del temp_df1, temp_df2
#sample image generation of variable aspect ratio
img_size = (image_height, image_width)
R_layer = np.ones(shape= img_size)*0.50
G_layer = np.ones(shape= img_size)*0.50
B_layer = np.ones(shape= img_size)*0.50
A_layer = np.ones(shape= img_size)
img = np.dstack([R_layer,G_layer,B_layer,A_layer])
#add a mark at the top left of the image
for k in range(0,3):
for i in range(0,int(image_width*0.2*data_AR)):
for j in range(0,int(image_width*0.2)):
img[i,j,k] = 0
#add a mark at the center of the image
# get center coordinates of the image
center = [[2, 4], [2, 5]]
for k in range(0,3):
for point in center:
if k == 0:
img[point[0],point[1],k] = 1
else:
img[point[0],point[1],k] = 0
#show image
fig, ax = plt.subplots()
ax.imshow(img)
###############################################################################
this is the code that generates the image:
#%%
# sample code
# at this point Iìve already loaded the image, the pix2scale value
# and the df containing data points
#read image aspect ratio
img_AR = img.shape[0]/img.shape[1]
pixel_width = img.shape[1]
pixel_height = img.shape[0]
# each pixel correspond to 0.3125 unit (mm)
pix2scale = 0.3125
#define image position
#the center of the image need to be placed at (0,0)
#bottom left corner
left = - (pixel_width * pix2scale)/2
bottom = - (pixel_height * pix2scale)/2
right = left + (pixel_width * pix2scale)
top = bottom + (pixel_height * pix2scale)
extent = [left,right,bottom,top]
#figure definition
figure_width = 15 #inch
figure_AR = 1
scatter_AR = img_AR
#initialize figure
fig_s= plt.figure(figsize = (figure_width,figure_width*figure_AR))
gs = plt.GridSpec (3,3)
#scatter plot in ax1
ax1 = fig_s.add_subplot(gs[:2,:2])
g = sns.scatterplot( data = df,
x = 'x',
y = 'y',
hue = 'label',
ax =ax1
)
ax1.invert_xaxis()
ax1.invert_yaxis()
#resize the figure box
box = ax1.get_position()
ax1.set_position([box.x0,box.y0,box.width*0.4,box.width*0.4*scatter_AR])
ax1.legend(loc = 'center left', bbox_to_anchor = (1,0.5))
ax1.set_title('Inclusions Scatter Plot')
ax1.set_aspect(scatter_AR)
#plt image
ax1.imshow(img,extent = extent)
#scatter plot
ax2 = fig_s.add_subplot(gs[2,:2])
g = sns.scatterplot( data = df,
x = 'x',
y = 'y',
hue = 'label',
ax =ax2
)
#resize the figure box
box = ax2.get_position()
ax2.set_position([box.x0,box.y0,box.width*0.4,box.width*0.4*scatter_AR])
ax2.legend(loc = 'center left', bbox_to_anchor = (1,0.5))
ax2.set_title('Inclusions Scatter Plot')
ax2.set_aspect(scatter_AR)
ax2.imshow(img,extent = extent)
#scatter plot
ax3 = fig_s.add_subplot(gs[1,2])
g = sns.scatterplot( data = df,
x = 'x',
y = 'y',
hue = 'label',
ax =ax3
)
#resize the figure box
box = ax3.get_position()
ax3.set_position([box.x0,box.y0,box.width*0.4,box.width*0.4*scatter_AR])
ax3.legend(loc = 'center left', bbox_to_anchor = (1,0.5))
ax3.set_title('Inclusions Scatter Plot')
ax3.set_aspect(scatter_AR)
ax3.imshow(img,extent = extent)
#add suptitle to figure
fig_s.suptitle('my title',fontsize= 22)
fig_s.subplots_adjust(top=0.85)
# #make it fancy
for i in range(3):
fig_s.tight_layout()
plt.pause(0.2)
I've plotted multiple grid because I wanted to test the tight_layout().
[enter image description here][2]

Getting RMagick/ImageMagick gravity with text

Here is Ruby code:
require 'rmagick'
include Magick
img = Image.new(300, 300)
draw = Draw.new
draw.line(0, 150, 300, 150)
draw.line(150, 0, 150, 300)
# for each of known gravity constants...
%w[NorthWestGravity NorthGravity NorthEastGravity WestGravity CenterGravity EastGravity SouthWestGravity SouthGravity SouthEastGravity].
each{|g|
# set gravity to this value...
draw.gravity Magick.const_get(g)
# ...and draw text with this constant name
draw.text 150, 150, g
}
draw.draw(img)
img.write('tmp/gravity.png')
Here is image which it produces:
For SouthEast/NorthWest and similar gravities result is as expected (text is near 150,150, moved in desired direction). But for South, North and others result is really pretty weird.
As far as I can understand from code, RMagick just translates gravity and text commands into corresponding ImageMagick drawing primitives, so, I suppose its something in ImageMagick's gravity concept that I can't get.
What is it?..
I suppose its something in ImageMagick's gravity concept that I can't get.
What is it?..
The key to understanding what's going on is to locate the CenterGravity text.
Shifted left by 150px, and down by 150px.
Now compare compare NorthWestGravity position.
Also translated left & down by 150px respectively. Seeing a trend?
Your issue is with this line...
draw.text 150, 150, g
The Magick::Draw API maps to MVG spec. Use Magick::Draw.push & Magick::Draw.pop to control drawing context.
Edit from comments...
For setting the origin of text to be drawing, you'll need to calculate the position after evaluation the text/type metrics.
Example.
require 'rmagick'
include Magick
img = Image.new(300, 300) {
self.background_color = "palegreen"
}
draw = Draw.new
dotes = Draw.new # Dotes added for point of origin
dotes.fill = "red"
cursor = 1
# for each of known gravity constants...
%w[NorthWestGravity NorthGravity NorthEastGravity WestGravity CenterGravity EastGravity SouthWestGravity SouthGravity SouthEastGravity].
each{|g|
offsetX = 150
offsetY = cursor * 25
dotes.circle offsetX, offsetY, offsetX+2, offsetY+2
# Get metrics of text
metrics = draw.get_type_metrics(img, g)
# Full width
if %w[NorthWestGravity WestGravity SouthWestGravity].include? g then
offsetX -= metrics[:width]
end
# Full height
if %w[SouthWestGravity SouthGravity SouthEastGravity].include? g then
offsetY += metrics[:ascent]
end
# Half width
if %w[NorthGravity SouthGravity CenterGravity].include? g then
offsetX -= metrics[:width] / 2
end
# Half height
if %w[WestGravity CenterGravity EastGravity].include? g then
offsetY += metrics[:ascent] / 2
end
draw.text offsetX, offsetY, g
cursor += 1
}
dotes.draw(img)
draw.draw(img)
img.write('output.png')

Skipping some axis labels in a plot with imagesc

I have created a big heat map using matlab's imagesc command. It plots the error output for each combination of the values in x and y axes. As can be seen in the figure there are too many axes labels. This might become even denser as I plan to increase the number of points in both x and y axes - which means I will get more outputs on a finer grid.
I want to be flexible with the labels, and skip some of them. I want to do this for both X and Y. I also want to be flexible with the "ticks" and draw either all of them or maybe skip some of them. Keep in mind that both the X and Y values are not increasing in order, at first the increment is 0.01 for 9 points, then 0.1, then 1 or 3 or whatever. I will change these increments too.
I tried to show what I want the graph look like in the second image. I want roughly the labels shown in red boxes only. As I said these are not set values, and I will make the increments smaller which will lead to denser plot.
Thank you for your help.
OS: Windows 7, 8 (64 bit)
Matlab version: Matlab 2014 a
You can manipulate the ticks and labels like this:
ticksarray=[1 33 41 100 ...] % edit these to whatever you want
tickslabels={'1', '33', '41', '100'; ...} % match the size of both arrays
set(gca,'XTick',ticksarray)
set(gca,'XTickLabel',tickslabels)
The same thing applies to the y-axis.
Small working example:
x=1:100;
y=2*x.^2-3*x+2;
plot(x,y)
ticksarray=[1 33 41 100];
tickslabels={'1', '33', '41', '100'};
set(gca,'XTick',ticksarray)
set(gca,'XTickLabel',tickslabels)
Example:
figure(1)
load clown
subplot(211)
imagesc(X);
subplot(212)
imagesc(X);
h = gca;
Now you can either set a maximum number of labels per axis:
%// define maximum number of labels
maxLabel = 3;
h.XTick = linspace(h.xlim(1),h.xlim(2),maxLabel);
h.YTick = linspace(h.ylim(1),h.ylim(2),maxLabel);
or define how many labels should be skipped:
%// define number of labels to skip
skipLabel = 2;
h.XTick = h.XTick(1:skipLabel:end);
h.YTick = h.YTick(1:skipLabel:end)
You can also get a different number of ticks and labels, more complicated though:
maxLabel = 3;
maxTicks = 6;
h.XTick = linspace(h.xlim(1),h.xlim(2),maxTicks);
h.YTick = linspace(h.ylim(1),h.ylim(2),maxTicks);
h.XTickLabel( setdiff( 1:maxTicks, 1:maxTicks/maxLabel:maxTicks ) ) = repmat({''},1,maxTicks-maxLabel);
h.YTickLabel( setdiff( 1:maxTicks, 1:maxTicks/maxLabel:maxTicks ) ) = repmat({''},1,maxTicks-maxLabel);
If you use a prior version of Matlab 2014b, then you will need the set command to set all properties:
%// define maximum number of labels
maxLabel = 3;
Xlim = get(h,'Xlim');
Ylim = get(h,'Ylim');
set(h,'XTick', linspace(Xlim(1),Xlim(2),maxLabel));
set(h,'YTick', linspace(Ylim(1),Ylim(2),maxLabel));
%// or define number of labels to skip
skipLabel = 2;
XTick = get(h,'XTick');
YTick = get(h,'YTick');
set(h,'XTick', XTick(1:skipLabel:end));
set(h,'YTick', YTick(1:skipLabel:end));
%// or combined
maxLabel = 3;
maxTicks = 6;
Xlim = get(h,'Xlim');
Ylim = get(h,'Ylim');
set(h,'XTick', linspace(Xlim(1),Xlim(2),maxTicks));
set(h,'YTick', linspace(Ylim(1),Ylim(2),maxTicks));
XTickLabel = cellstr(get(h,'XTickLabel'));
YTickLabel = cellstr(get(h,'YTickLabel'));
XTickLabel( setdiff( 1:maxTicks, 1:maxTicks/maxLabel:maxTicks ),: ) = repmat({''},1,maxTicks-maxLabel);
YTickLabel( setdiff( 1:maxTicks, 1:maxTicks/maxLabel:maxTicks ),: ) = repmat({''},1,maxTicks-maxLabel);
set(h,'XTickLabel',XTickLabel);
set(h,'YTickLabel',YTickLabel);
After applying the second method proposed by #thewaywewalk I got the second figure below. Apparently the labels need to be structured as well, because they only take the first so many labels.
Then I tried to manipulate the labels as shown below, and got the third image.
skipLabel = 2;
XTick = get(h,'XTick');
YTick = get(h,'YTick');
set(h,'XTick', XTick(1:skipLabel:end));
set(h,'YTick', YTick(1:skipLabel:end));
XTickLabel = get(h,'XTickLabel');
labelsX = cell( length(1: skipLabel:length(XTick)) , 1);
j = 1;
for i = 1: skipLabel:length(XTick)
labelsX{j} = XTickLabel(i, :);
j = j + 1;
end
set(h,'XTickLabel', labelsX);
YTickLabel = get(h,'YTickLabel');
labelsY = cell( length(1: skipLabel:length(YTick)) , 1);
j = 1;
for i = 1: skipLabel:length(YTick)
labelsY{j} = YTickLabel(i, :);
j = j + 1;
end
set(h,'YTickLabel', labelsY);
The Y axis labels seem to be in place as before (right next to tick), however the X axis labels seem to be shifted to the left a little. How can I correct this?
Another note: How can I change the scientific values into normal numbers? Also, probably there is a better approach at manipulating the labels.

matlab plot graph of data over an image

What I would like to do is plot an image of a graph (from say a pdf file or a scanned image). Next, I would like to overlay an axis on the graph in the image, and then plot data on that axis (over the image).
Using imtool, I know the coordinates of the graph in the image (x range = ~52-355 pixels, and y range = 23(top) - 262(bottom) pixels in this case).
This is what I have tried:
I = imread('C:\MATLAB\R2014a\help\images\ref\ftrans2_fig.png');
I = squeeze(uint8(mean(I,3)));
figure, imshow(I)
[rows, cols] = size(I);
x_data = (-1 : .01 : +1)';
y_data = 1 - x_data.^2;
h1 = axes('Position',([52, 23, 355-52, 262-23] ./ [cols, rows, cols, rows] ));
set(h1, 'Color', 'none')
hold on
plot(x_data, y_data, '-rx')
Question: Knowing the pixel coordinates of the graph in the image, how do I determine the proper position of the axis in the figure, (my code fails to account for the actual size of the figure box, the gray border around the image). I have to do this for several images and sets of data, so I would like an automated method, assuming I find the coordinates of the graphs in the image ahead of time.
Thanks for your reply! (1st time posting, please be kind)
You may be able to solve your problem by forcing the image onto the same axis as the plot. Try this:
I = imread('C:\MATLAB\R2014a\help\images\ref\ftrans2_fig.png');
I = squeeze(uint8(mean(I,3)));
[rows, cols] = size(I);
x_data = (-1 : .01 : +1)';
y_data = 1 - x_data.^2;
h1 = axes('Position',([52, 23, 355-52, 262-23] ./ [cols, rows, cols, rows] ));
set(h1, 'Color', 'none')
hold on
image(I, 'Parent', h1);
plot(h1, x_data, y_data, '-rx')
That should at ensure that the plot axis and the image axis have the same origin, as they will be one and the same. You may need to adjust your sizing code. Let me know if that doesn't do it for you.
Good Luck!
I think I have it figured out.
It would have been easier if I could use:
figure, h1=imshow(I)
get(h1,'Position')
but that results in "The name 'Position' is not an accessible property for an instance of class 'image'."
Instead, this appears to work:
I = imread('C:\MATLAB\R2014a\help\images\ref\ftrans2_fig.png');
I = squeeze(uint8(mean(I,3)));
in_mag = 300;
figure, imshow(I, 'Border', 'tight', 'InitialMagnification', in_mag)
[rows, cols] = size(I);
x_data = (-1 : .01 : +1)';
y_data = 1 - x_data.^2;
% Coord of graph in image pixels
x_0 = 50; x_max = 354; y_0 = 262; y_max = 23;
h1 = axes('Position',([x_0, rows-y_0, x_max-x_0, y_0-y_max] ...
./ [cols, rows, cols, rows] ));
set(h1,'Color','none')
hold on
plot(x_data, y_data, '-rx')
ylim([0,1.4])
set(gca,'YColor', [0 0 1], 'XColor', [0 0 1])
However, if anybody has a better idea, I would be very happy to explore it!
Thanks

Matlab: Something like "relative" position with uicontrol/axis; keep fixed margins when resizing

I currently have a big headache to get a small GUI working nicely which isn't being created with GUI editor but programmatically! What I have so far is something like the following:
hFig = figure();
set(hFig, 'Position', [300 200 500 400]);
plot((1:10).^2, '*-r');
% Größe des Plots so anpassen, dass links Platz für Buttons
ap = get(gca, 'TightInset');
fp = get(gcf, 'Position');
set(gca, 'Position', [160/fp(3), 30/fp(4), (fp(3)-180)/fp(3), (fp(4)-60)/fp(4)]);
uicontrol('Style', 'pushbutton', 'String', 'foo', 'Position', [15 fp(4)-60 110 30]);
uicontrol('Style', 'pushbutton', 'String', 'bar', 'Position', [15 fp(4)-100 110 30]);
Try to resize it: It doesn't 'look' the same, which means that the uicontrol boxes don't stay at the same relative position and the margins from the axis to the figure window get bigger. What I want to achieve is:
Have a figure window with a given position (x/y, width and height) with a plot inside. The plot will have a title and labels for x and y. Make the plot as height and width to have the TightInset plus a margin in each direction of a certain px-size (e.g. TightInset + 10px) as big as the figure window; except leave 150px of free space on the left to place some uicontrol buttons, and have them stay in the same position: This would be the same as being able to give the position from top/left (top = 20, left = 10) instead of bottom/left.
Thanks a lot!
Okay finally found a working solution I wanted it to be :-) Hopefully it is helpfull for somebody interested in it:
Main script file:
p = [300 300 1000 600];
fixedMargins = [250 0 0 0]; % [left, top, right, bottom]
f = figure('Position', p, 'Color', [0.9 0.9 0.9]);
plot(-10:10, (-10:10).^3, '*-r');
set(f, 'ResizeFcn', {#resizeCallback, gca, fixedMargins, {#myuiFunc, f, 40, 50}});
title('bla')
xlabel('foooooooooo')
ylabel('barrrrrrr')
Resize Callback Function:
% Need to pass the handle of the axis to modify (hAx) AND to pass the
% desired margins as second extra callback argument:
% [left, top, right, bottom]!
function resizeCallback(hFig, ~, hAx, fixedMargins, func)
% Disable automatic rezising
set(hAx, 'Units', 'pixels');
% Figure-Size
fp = get(hFig, 'Position');
% Calculate Position of the axis
margin = get(hAx, 'TightInset') * [-1 0 1 0; 0 -1 0 1; 0 0 1 0; 0 0 0 1];
% Position to fill the figure minus the TightInset-Margin
newPos = [0 0 fp(3:4)] - margin;
% Change position based on margins
newPos(1) = newPos(1) + fixedMargins(1);
newPos(3) = newPos(3) - fixedMargins(1) - fixedMargins(3);
newPos(2) = newPos(2) + fixedMargins(4);
newPos(4) = newPos(4) - fixedMargins(2) - fixedMargins(4);
% Set new position
set(hAx, 'Position', newPos);
% Call UI-Func
if(nargin == 5)
f = func{1};
args = func(2:end);
f(args{:});
end
end
You can pass whatever function you want to be called when resizing the figure window, e.g. to update something in the figure. In my example it's the myuiFunc(), which is the following:
function myuiFunc(hFig, left, top)
persistent handles;
if(~isempty(handles))
delete(handles);
handles = [];
end
fp = get(hFig, 'Position');
h1 = uicontrol('Style', 'pushbutton', 'String', 'Foo','Position', [left fp(4)-top 100 35]);
h2 = uicontrol('Style', 'pushbutton', 'String', 'Bar','Position', [left fp(4)-top-50 100 35]);
handles = [h1 h2];
end
I like it :) Hopefully you too!
Edit: No need to edit the resizeCallback Function! Should work if you just pass your desired margins to it and if you like, additionally a function handle with arguments which will be called for each resize!
You can also use "Normalized" units.
uicontrol('Style', 'pushbutton', 'String', 'foo', 'Units','normalized','Position', [0.90 0.05 0.08 0.08] );

Resources