I am using the following code to generate a legend in a separate file. Unfortunately there is a black line in the middle of the legend that has been generated. In the middle of 'permissions modification'. How would i remove this black line ?
import matplotlib.pyplot as plt
plt.rcParams['font.family'] = 'serif'
plt.rcParams['font.size'] = 10
plt.rcParams['font.serif'] = ['Times New Roman'] + plt.rcParams['font.serif']
"#0C99BA"
"#2f4b7c"
"darkviolet"
"a05195"
"crimson"
"f95d6a"
"ff7c43"
"ffa600"
colors = ["#88D18A", "#0C99BA", "#2f4b7c", "darkviolet", "#a05195", "crimson", "#f95d6a", "#ff7c43", "#ffa600"]
f = lambda m,c: plt.plot([],[],marker=m, color=c, ls="none")[0]
handles = [f("s", colors[i]) for i in range(9)]
#handles = ["red", "red", "wine"]
#labels = ["red", "red", "wine"]
labels = ['Delayed\nExecution', 'File\nOpening', 'Firewall\nModification', 'Permission\nModification', 'Persistence', 'Proxied\nExecution', 'Reconnaissance', 'Registry\nModification', 'Task\nStopping']
legend = plt.legend(handles, labels, loc=3, framealpha=1, frameon=False,ncol=9,handletextpad=0.1,columnspacing=0)
def export_legend(legend, filename="legend.pdf"):
fig = legend.figure
fig.canvas.draw()
bbox = legend.get_window_extent().transformed(fig.dpi_scale_trans.inverted())
fig.savefig(filename, dpi="figure", bbox_inches=bbox)
export_legend(legend)
plt.show()
This is the infuriating and inexplicable black line:
Related
I have a plot made up of 3 choropleth subplots next to each other. I set the overall height and width to my desired dimensions (800 x 400 pixels). I want each subplot to go from top to bottom, but as it stands, the subplots retain the aspect ratio of 2:1, meaning I have wide margins at top and bottom. Those I want to remove.
As a minimum example, I am attaching the data and plot code:
The toy dataset:
import geopandas as gpd
from shapely.geometry.polygon import Polygon
minidf = gpd.GeoDataFrame(dict(
krs_code = ["08111", "08118"],
m_rugged = [42.795776, 37.324421],
bip = [83747, 43122],
cm3_over_1999 = [47.454688, 47.545940],
geometry = [Polygon(((9.0397, 48.6873),
(9.0397, 48.8557),
(9.3152, 48.8557),
(9.3152, 48.6873),
(9.0397, 48.6873))),
Polygon(((8.8757, 48.7536),
(8.8757, 49.0643),
(9.4167, 49.0643),
(9.4167, 48.7536),
(8.8757, 48.7536)))]
)).set_index("krs_code")
The plotting code:
import json
from plotly.subplots import make_subplots
import plotly.graph_objects as go
fig = make_subplots(rows = 1, cols = 3,
specs = [[{"type": "choropleth"}, {"type": "choropleth"}, {"type": "choropleth"}]],
horizontal_spacing = 0.0025 )
fig.update_layout(height = 400, width = 800,
margin = dict(t=0, r=0, b=0, l=0),
coloraxis_showscale=False )
for i, column in enumerate(["m_rugged", "cm3_over_1999", "bip"]):
fig.add_trace(
go.Choropleth(
locations = minidf.index,
z = minidf[column].astype(float), # Data to be color-coded
geojson = json.loads(minidf[["geometry"]].to_json()),
showscale = False
),
col = i+1, row = 1)
fig.update_geos(fitbounds="locations", visible=True)
fig.show()
Notice the margins at top and bottom, which retain the aspect ratio of each subplot, while they are supposed to stretch from top to bottom:
I tried several parameters within go.Choropleth() and .update_layout(), but to no avail.
I have a plotly plot which looks like this:
The Code I am using is below:
fig = make_subplots(specs=[[{"secondary_y": True}]])
fig.add_trace(go.Scatter( x = pf['Timestamp'], y = pf['Price_A'], name ='<b>A</b>',
mode = 'lines+markers',
marker_color = 'rgba(255, 0, 0, 0.8)',
line = dict(width = 3 ), yaxis = "y1"),
secondary_y=False,)
fig.add_trace(go.Scatter( x = df['Timestamp'], y = df['Price_B'], name='<b>B</b>',
mode = 'lines+markers',
marker_color = 'rgba(0, 196, 128, 0.8)',
line = dict(width = 3 ), yaxis = "y1") ,
secondary_y=False,)
for i in pf2['Timestamp']:
fig.add_vline(x=i, line_width=3, line_dash="dash", line_color="purple",
name='Event')
fig.update_layout( title="<b>Change over Time</b>", font=dict( family="Courier New,
monospace", size=16, color="RebeccaPurple"),
legend=dict(
yanchor="top",
y=0.99,
xanchor="left",
x=0.01
))
How can I add the entry in the legend for the event that is denoted by the vertical lines?
When you use add_vline, you are adding an annotation which will not have a corresponding legend entry.
You'll need to instead use go.Scatter to plot the vertical lines, passing the minimum and maximum values in your data (plus or minus some padding) to the y parameter. Then you can set this same y-range for your plot. This will give you the appearance of vertical lines while still showing the full range of your data.
Update: you can use a legend group so that the vertical lines appear as a single entry in the legend
For example:
from pkg_resources import yield_lines
import plotly.express as px
import plotly.graph_objects as go
fig = go.Figure()
df = px.data.stocks()
for col in ['GOOG','AMZN']:
fig.add_trace(go.Scatter(
x=df['date'],
y=df[col]
))
vlines = ["2018-07-01","2019-04-01","2019-07-01"]
min_y,max_y = df[['GOOG','AMZN']].min().min(), df[['GOOG','AMZN']].max().max()
padding = 0.05*(max_y-min_y)
for i,x in enumerate(vlines):
fig.add_trace(go.Scatter(
x=[x]*2,
y=[min_y-padding, max_y+padding],
mode='lines',
line=dict(color='purple', dash="dash"),
name="vertical lines",
legendgroup="vertical lines",
showlegend=True if i == 0 else False
))
fig.update_yaxes(range=[min_y-padding, max_y+padding])
fig.show()
I am making a 3d scatterplot with Matplotlib, with the following code:
fig = plt.figure()
ax = fig.add_subplot(projection='3d')
for i in range(len(model.ut[0,:]-1)):
x_dp = model.ut[0,i]
y_dp = model.ut[1,i]
z_dp = model.ut[2,i]
ax.scatter(x_dp, y_dp, z_dp, marker='^')
for i in range(len(model.cluster_centers[:,0]-1)):
x_c = model.cluster_centers[i,0]
y_c = model.cluster_centers[i,1]
z_c = model.cluster_centers[i,2]
ax.scatter(x_c, y_c, z_c, marker='o')
ax.set_xlabel('Dimension 0')
ax.set_ylabel('Dimension 1')
ax.set_zlabel('Dimension 2')
ax.set_title('3d')
pyplot.show()
Where model.ut and cluster_center are matrices with the data that I want to visualize.
Currently, the color of each datapoint is different:
Instead, I would like each point with the same marker to be the same color (like it has been done here). How can I do this?
I am using plotly in Python 3.6.3 and am trying to do a Choropleth map as in here. I would like to change the attributes of what appears when hovering above the map. That is, for example, if we consider the first map and hover of California, it looks like:
I want to change both the font size of the content that appears and the size of the box. Is there a way to access those?
Here is the code that generates it:
import plotly.plotly as py
import pandas as pd
df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/2011_us_ag_exports.csv')
for col in df.columns:
df[col] = df[col].astype(str)
scl = [[0.0, 'rgb(242,240,247)'],[0.2, 'rgb(218,218,235)'],[0.4, 'rgb(188,189,220)'],\
[0.6, 'rgb(158,154,200)'],[0.8, 'rgb(117,107,177)'],[1.0, 'rgb(84,39,143)']]
df['text'] = df['state'] + '<br>' +\
'Beef '+df['beef']+' Dairy '+df['dairy']+'<br>'+\
'Fruits '+df['total fruits']+' Veggies ' + df['total veggies']+'<br>'+\
'Wheat '+df['wheat']+' Corn '+df['corn']
data = [ dict(
type='choropleth',
colorscale = scl,
autocolorscale = False,
locations = df['code'],
z = df['total exports'].astype(float),
locationmode = 'USA-states',
text = df['text'],
marker = dict(
line = dict (
color = 'rgb(255,255,255)',
width = 2
) ),
colorbar = dict(
title = "Millions USD")
) ]
layout = dict(
title = '2011 US Agriculture Exports by State<br>(Hover for breakdown)',
geo = dict(
scope='usa',
projection=dict( type='albers usa' ),
showlakes = True,
lakecolor = 'rgb(255, 255, 255)'),
)
fig = dict( data=data, layout=layout )
py.iplot( fig, filename='d3-cloropleth-map' )
The chloropleth>hoverlabel function lets you set the background color, border color, and font. The size of the border box is determined by the text within it, however. If the name shows up as truncated it can be expanded with the chloropleth>hoverlabel>namelength function.
I have three plots based on the same dataset. How can I link all three plots so that when I select a certain species in vbar plot, two scatter plot also change to plot points in that species only.
any help is appreciated~
from bokeh.sampledata.iris import flowers
from bokeh.plotting import figure, output_file, show
from bokeh.models import ColumnDataSource, CategoricalColorMapper
from bokeh.layouts import column, row
#color mapper to color data by species
mapper = CategoricalColorMapper(factors = ['setosa','versicolor', 'virginica'],\
palette = ['green', 'blue', 'red'])
output_file("plots.html")
#group by species and plot barplot for count
species = flowers.groupby('species')
source = ColumnDataSource(species)
p = figure(plot_width = 800, plot_height = 400, title = 'Count by Species', \
x_range = source.data['species'], y_range = (0,60),tools = 'box_select')
p.vbar(x = 'species', top = 'petal_length_count', width = 0.8, source = source,\
nonselection_fill_color = 'gray', nonselection_fill_alpha = 0.2,\
color = {'field': 'species', 'transform': mapper})
labels = LabelSet(x='species', y='petal_length_count', text='petal_length_count',
x_offset=5, y_offset=5, source=source)
p.add_layout(labels)
#scatter plot for sepal length and width
source1 = ColumnDataSource(flowers)
p1 = figure(plot_width = 800, plot_height = 400, tools = 'box_select', title = 'scatter plot for sepal')
p1.circle(x = 'sepal_length', y ='sepal_width', source = source1, \
nonselection_fill_color = 'gray', nonselection_fill_alpha = 0.2, \
color = {'field': 'species', 'transform': mapper})
#scatter plot for petal length and width
p2 = figure(plot_width = 800, plot_height = 400, tools = 'box_select', title = 'scatter plot for petal')
p2.circle(x = 'petal_length', y ='petal_width', source = source1, \
nonselection_fill_color = 'gray', nonselection_fill_alpha = 0.2, \
color = {'field': 'species', 'transform': mapper})
#show all three plots
show(column(p, row(p1, p2)))
I don't think there's some functionality existing for this at the moment. But you can explicitly link two ColumnDataSources with a CustomJS callback:
from bokeh.models import CusomJS
source = ColumnDataSource(species)
source1 = ColumnDataSource(flowers)
source.js_on_change('selected', CustomJS(args=dict(s1=source1), code="""
const indices = cb_obj.selected['1d'].indices;
const species = new Set(indices.map(i => cb_obj.data.species[i]));
s1.selected['1d'].indices = s1.data.species.reduce((acc, s, i) => {if (species.has(s)) acc.push(i); return acc}, []);
s1.select.emit();
"""))
Note that this callback only synchronizes selection from the bar plot to the scatter plots. To make selections on the scatter plots influence the bar plot, you'll have to write some additional code.