Visualize a 408x408 numpy array as a heatmap - python

Hello I want to visualize the sandbox game map. I have collected the data from API and now I want to create a heatmap kind of visualization, where the color changes depending on how many times the land's been sold. I'm looking for a Python tool / GUI that will let me visualize a 408x408 numpy array. I've tried the seaborn heatmap, but it doesn't look clean (see image), even If I try to set figsize to (200, 200) it's not big enough for my needs. I want to have a visualization on potentially whole screen, where each land is big enough so that I can write something on it (potentially price). Better option would be to have a big map with sliders.
Perhaps it's possible to do what I want using Seaborn's heatmap, but I'm not very familiar with it.
Here's the code I used for visualization:
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
arr = np.random.rand(408, 408)
x_labels = list(range(-204, 204))
y_labels = list(reversed(range(-204, 204)))
fig, ax = plt.subplots(figsize=(100, 100))
sns.heatmap(arr, square=True, xticklabels=x_labels, yticklabels=y_labels, ax=ax)
ax.tick_params(axis="both", labelsize=40)

Visualizing such large data with seaborn or Matplotlib will be difficult.
For that, we can use Plotly and the dash python library. So, we can add a slider to view some portion of data at a time.
I have used these two libraries.
import plotly.express as px
from dash import Dash, dcc, html, Input, Output
import numpy as np
import pandas as pd
#creating data
arr = np.random.rand(408, 408)
x_labels = list(range(-204, 204))
y_labels = list(reversed(range(-204, 204)))
#Converted to dataframe
df_data = pd.DataFrame(arr,index =y_labels, columns = [x_labels] )
app = Dash(__name__)
#How many items to show at a time
show_item_limit = 20
app.layout = html.Div([
html.H4('Range'),
dcc.Graph(id="graph"),
html.P("Select range"),
dcc.Slider(
min = 0,
max = 408-show_item_limit,
step = show_item_limit,
value = 0,
id= 'my-slider'
),
])
#app.callback(
Output("graph", "figure"),
Input("my-slider", "value"))
def filter_heatmap(selected_value):
# Selected value will be passed from Slider
df = df_data # replace with your own data source
#We can filter the data here
filtered_df = df_data.iloc[selected_value:selected_value+show_item_limit,range(selected_value,selected_value+show_item_limit)]
#Update using plotly
fig = px.imshow(filtered_df,
text_auto=True,
labels=dict(x="X-range", y="y-range"),
x = filtered_df.columns,
y = filtered_df.index
)
return fig
app.run_server(debug=True)
See the output image: Output from code

Related

How to add sucesive spots in plotly (python) using slider?

I am trying to make a plotly graph which could plot the evolution of a parameter's value along a period of time.
By now, what I've managed to get is plotting individual points. However, I'd like to keep the previos spots plotted while getting an increment on the slider.
My code:
import numpy as np
import plotly.express as px
import pandas as pd
df=pd.read_excel("file.xlsx")
fig=px.scatter(df, x="$R[\Omega]$", y="$X[\Omega]$", title='$U_1/I_1$',animation_frame="t[seg]")
fig["layout"].pop("updatemenus")
fig.show()
Data
df2=pd.DataFrame({"$R[\Omega]$":[-0.092034,-0.096416,-0.103026],
"$X[\Omega]$":[0.045707,0.047590,0.039953],
"t[seg]":[0.416244,0.417078,0.417912]})
have simulated data as I don't have access to your excel spreadsheet. Named columns consistently with what your code implies
need to make sure layout has axes ranges set for all data
after building figure, for each frame update the data in traces such that it contains all values from previous frames as well. (Note this assume that t[seg] is numeric)
full code
import numpy as np
import plotly.express as px
import pandas as pd
# df=pd.read_excel("file.xlsx")
# generate data in structure implied by question
df = pd.DataFrame(
{
"$R[\Omega]$": np.linspace(0, 100, 250),
"$X[\Omega]$": np.sin(np.linspace(0, 50, 250)),
"t[seg]": np.linspace(0, 100, 250) // 10,
}
)
xcol = "$R[\Omega]$"
ycol = "$X[\Omega]$"
fcol = "t[seg]"
fig = px.scatter(
df, x=xcol, y=ycol, title="$U_1/I_1$", animation_frame=fcol
)
fig["layout"].pop("updatemenus")
# prepare axes to show all values
fig.update_layout(
{
ax: {
"range": [
df[col].min(),
df[col].max(),
]
}
for ax,col in zip(["xaxis", "yaxis"], [xcol,ycol])
}
)
# add data into each frame from all preceeding traces
for fr in fig.frames[1:]:
df_ = df.loc[df[fcol].le(float(fr.name))]
for t in fr.data:
t.update(x=df_[xcol].values, y=df_[ycol].values)
fig

Interactive plotly boxplot with ipywidgets

I am trying to create an interactive boxplot with ipywidgets and Plotly.
I started by looking at this example
While this is fine, I'd like to change the groupings of the boxplot based on a dropdown input.
With interact I can do this:
import datetime
import numpy as np
import pandas as pd
import plotly.graph_objects as go
from ipywidgets import widgets
df = pd.read_csv(
'https://raw.githubusercontent.com/yankev/testing/master/datasets/nycflights.csv')
df = df.drop(df.columns[[0]], axis=1)
from ipywidgets import interact
def view_image(col):
fig = go.FigureWidget()
for val in df[col].unique():
groupData = df.query(f'{col} == "{val}"')
fig.add_trace(
go.Box(y = groupData['distance'],
name = val)
)
fig.show()
interact(view_image, col = ['origin', 'carrier'])
And the result is that I can change the column based on which the data is grouped.
However, I would like to have more control on the widgets, like in the official example.
This is what I am trying (and failing):
# Assign an empty figure widget with two traces
gdata = []
for origin in df.origin.unique():
groupData = df.query(f'origin == "{origin}"')
gdata.append(
go.Box(y = groupData['distance'],
name = origin)
)
g = go.FigureWidget(data=gdata,
layout=go.Layout(
title=dict(
text='NYC FlightDatabase'
),
barmode='overlay'
))
def response_box(change):
col = column.value
with g.batch_update():
gdata = []
for val in df[col].unique():
groupData = df.query(f'{col} == "{val}"')
gdata.append(
go.Box(y = groupData['distance'],
name = val)
)
g.data = gdata
column = widgets.Dropdown(
options=['origin','carrier']
)
column.observe(response_box, 'value')
container2 = widgets.HBox([column])
widgets.VBox([container2,
g])
Note that since I have new groupings, I cannot just go into g.data[index].y and change per index, but I have to re-generate the figure as in the interact function.
This particular iteration gives me a "you cannot update data directly" error. I tried in a few different ways, but I don't seem to find one that works.
Any idea?
it's not clear how you want to interact with the dimensions of data. So I've gone with defining x and color of figure, plus filtering by origin, dest, carrier
box plots are far simpler to create using Plotly Express so have used that
it then really simplifies to passing parameters. Have used https://ipywidgets.readthedocs.io/en/latest/examples/Using%20Interact.html with decorator
import datetime
import numpy as np
import pandas as pd
import plotly.graph_objects as go
import plotly.express as px
from ipywidgets import widgets
from ipywidgets import interact
df = pd.read_csv(
"https://raw.githubusercontent.com/yankev/testing/master/datasets/nycflights.csv"
)
df = df.drop(df.columns[[0]], axis=1)
#interact
def view_image(
col=widgets.Dropdown(
description="Plot:", value="carrier", options=["origin", "carrier"]
),
filtercol=widgets.Dropdown(
description="Filter by:", value="carrier", options=["origin", "dest", "carrier"]
),
filter=widgets.Text(
description="Filter:", value=""
),
):
# check if filter results in any rows... if not all data...
if df[filtercol].eq(filter).any():
dfp = df.loc[df[filtercol].eq(filter)]
else:
dfp = df
fig = px.box(dfp, x=col, y="distance", color=col)
go.FigureWidget(fig.to_dict()).show()

Plotly bar chart not ascending/descending

I have a bar chart in plotly that I have produced, however, it is not in any type of order. How would I sort to ascending or descending?
What I am doing:
fig = px.bar(data, x='Old_SKU', y='u_power')
fig = data.sort_values('u_power', ascending=True)
fig.show()
I'm not sure what your desired output is, or what your data looks like. In any case fig in plotly terms is normaly a plotly figure object. When you're running fig = data.sort_values('u_power', ascending=True) you're not building a figure, but sorting a dataframe. So far I can only imagine that you'd like to sort a dataset that looks like this:
... into this:
Or maybe you're expecting a continuous increase or decrease? In that case you will have to share a dataset. Nevertheless, with a few tweaks depending on your dataset, the following snippet should not be far from a working solution:
import plotly.express as px
import numpy as np
import pandas as pd
var = np.random.randint(low=2, high=6, size=20).tolist()
data = pd.DataFrame({'u_power':var,
'Old_SKU':np.arange(0, len(var))})
# fig = px.bar(data, x='Old_SKU', y='u_power', barmode='stack')
fig = px.bar(data.sort_values('u_power'), x='Old_SKU', y='u_power', barmode='stack')
fig.show()

Scatter 3d in Plotly Express - Colab Notebook is not plotting with equal axes

Trying to plot a very simple 3d scatter plot with x,y,z axes all equal in length. It is not working.
Code:
'''
from mpl_toolkits import mplot3d
import pandas
from pandas import DataFrame
pt_num = 100
x = np.random.uniform(-10,10,size=(pt_num,2))
model = np.array([2,2]).reshape(-1,1)
y = np.dot(x,model)
data = np.hstack((x,y))
dats = {'x':data[:,0].squeeze(),'w':data[:,1].squeeze(),'y':data[:,2].squeeze()}
df = DataFrame(data=dats)
import plotly.express as px
fig = px.scatter_3d(df, x='x', y='w', z='y',width=1200, height=1200)
fig.update_layout(scene=dict(xaxis=dict(range=[-10,10]),yaxis=dict(range=[-10,10]),zaxis=dict(range=[-10,10])))
fig['layout'].update(width=1500, height=1500, autosize=False)
fig.show()
'''
Thank you for any help.
Asked too soon out of frustration. The answer is to add 'aspectmode="cube"' to the scene dict for fig.update_layout:
fig.update_layout(scene=dict(xaxis=dict(range=view_range),yaxis=dict(range=view_range),zaxis=dict(range=view_range),aspectmode="cube"))

Plotly: How to plot a cumulative "steps" histogram?

I am trying to plot a cumulative histogram using Plotly in python, but make it look like "steps", i.e. bars with no color and only the top line is displayed. Something like this:
Basically, I'm trying to reproduce the behavior of the following matplotlib code:
import matplotlib.pyplot as plt
plt.hist(x, cumulative=True, histtype='step')
So far, the best I've been able to do is:
import plotly.graph_objs as go
from plotly.offline import iplot
h = go.Histogram(x=x,
cumulative=dict(enabled=True),
marker=dict(color="rgba(0,0,0,0)",
line=dict(color="red", width=1)))
iplot([h])
Which results in something like:
So what's the trick?
If you're willing to handle the binning and accumulation before you plot the data, you can use a go.Scatter object with the shape property of the line set to 'hvh'.
Plot:
Code: Setup for a Jupyter Notebook
#imports
import plotly.plotly as py
import plotly.graph_objs as go
from plotly.offline import download_plotlyjs, init_notebook_mode, plot, iplot
import numpy as np
import pandas as pd
# qtconsole for debugging
#%qtconsole -- style vim
# Notebook settings
init_notebook_mode(connected=True)
# Some sample data
x = np.random.normal(50, 5, 500)
binned = np.histogram(x, bins=25, density=True)
plot_y = np.cumsum(binned[0])
# Line
trace1 = go.Scatter(
x=binned[1],
y=plot_y,
mode='lines',
name="X",
hoverinfo='all',
line=dict(color = 'rgb(1255, 0, 0)', shape='hvh'
)
)
data = [trace1]
# Layout
layout = dict(title = 'Binned data from normal distribution',
legend=dict(
y=0.5,
traceorder='reversed',
font=dict(
size=16
)
)
)
# Make figure
fig = dict(data=data, layout=layout)
# Plot
iplot(fig, filename='line-shapes')
I hope this is something you can use!
Don't hesitate to let me know if not.
Some details:
The data sample is made using np.random.normal(). x is a sampled normal distribution with mean = 50, sigma = 5 and 500 observations. x is then put in 50 bins using np.histogram() which returns two arrays. These are used as data source for the plot.
Possible alternative approaches:
I also tried using your snippet with some random sample data and include shape='hvh' in your line=dict(color="red", width=1). That did not seem to work though. I also considered modifying the layout of your go.Histogram() so that only the top line of the bars were plotted, but I don't think it's possible.
The accepted solution works but may be limiting as the bins are all of equal width. One approach is to use matplotlib to calculate stats, then plot with plotly:
# sample data
# I am not using a normal distribution on purpose so that the effect of varying bin widths is apparent.
x = np.random.rand(100)
# use matplotlib to get "n" and "bins"
# n_bins will affect the resolution of the cumilative histogram but not dictate the bin widths.
n_bins = 100
n, bins, patches = plt.hist(x, n_bins, density=True, histtype='step', cumulative=-1)
# use plotly (v3) to plot
data = []
trace = go.Scatter(
x=bins,
y=n,
mode='lines',
name= "test",
line=dict(
shape='hvh'
)
)
data.append(trace)
fig = go.Figure(data=data)
iplot(fig)
The result should look something like this:

Categories