Related
I started using plotly to draw sankey charts.
I want to display a multi year series with birth and death year by year.
The current code looks like:
`
import plotly.graph_objects as go
fig = go.Figure(data=[go.Sankey(
node = dict(
pad = 15,
thickness = 20,
line = dict(color = "black", width = 0.5),
label = ["v11","v21","v31","v41","out21","out31","out41","in21","in31","in41", "v12", "v22"],
color = "blue"
),
link = dict(
source = [0, 7, 0, 1, 8, 1, 2, 9, 2, 10], #
target = [1, 1, 4, 2, 2, 5, 3, 3, 6,11],
value = [1000, 100, 100, 1000, 150, 50, 1000, 120, 80, 800]
))])
fig.update_layout(title_text="Basic Sankey Diagram", font_size=10)
fig.show()
`
which produces something like:
Sankey
All birth nodes are attached to the left side and the out nodes to the right side.
It should however look like this:
Sankey manually rearranged
Is there a way to pin the x-axis?
Or any d3 example, which does the trick?
Thanks for any hint,
Carl
PS: With the following question I got one step further [https://stackoverflow.com/questions/61152889/plotly-how-to-set-node-positions-in-a-sankey-diagram].
The look like this now:
import plotly.graph_objects as go
unique_list = ["c0_v_2021","c0_v_2022","c0_v_2023","c0_v_2024","c0_o_2022","c0_o_2023","c0_o_2024","c0_i_2022","c0_i_2023","c0_i_2024"]
title_list = ["Vol 2021","Vol 2022","Vol 2023","Vol 2024","Out 2022","Out 2023","Out 2024","In 2022","In 2023","In 2024"]
sources = [0, 7, 0, 1, 8, 1, 2, 9, 2]
targets = [1, 1, 4, 2, 2, 5, 3, 3, 6]
values = [1000, 100, 100, 1000, 150, 50, 1000, 120, 80]
## correction based on type
def my_corr(node_corr, steps):
x_corr = []
y_corr = []
for ftype in node_corr:
xcorr = 0
ycorr = 0
if ftype == 'i':
xcorr = - steps/3
ycorr = -0.2
x_corr.append(xcorr)
y_corr.append(ycorr)
return x_corr , y_corr
def my_nodify (node_names):
# node_names = unique_list.copy()
# unique name endings
## get year
ends = sorted(list(set([e[-4:] for e in node_names])))
## get type
corr = (list(([e[-6] for e in node_names])))
min, max = ends[0], ends[-1]
#intervals
steps = 1/((int(max)-int(min)))
x_corr, y_corr = my_corr(corr, steps)
# x-values for each unique name ending for input as node position
nodes_x = {}
xVal = 0
for e in ends:
nodes_x[str(e)] = xVal
xVal += steps
#x and y values in list form
x_values = [nodes_x[n[-4:]] for n in node_names]
## apply x adjustments
x_values_c = [x_values[i] + x_corr[i] for i in range(len(x_corr))]
y_values = []
y_val = 0
for n in node_names:
y_values.append(y_val)
y_val+=.001
y_values.append(y_val)
## apply y adjustments
y_values_c = [y_values[i] + y_corr[i] for i in range(len(y_corr))]
# y_values_c = y_values
return x_values_c, y_values_c
nodified = my_nodify(unique_list)
# plotly setup
fig = go.Figure(data=[go.Sankey(
arrangement='snap',
# arrangement='perpendicular',
node = dict(
pad = 5,
thickness = 20,
line = dict(color = "black", width = 0.5),
label = title_list,
color = "blue",
x=nodified[0], y=nodified[1]
),
link = dict(
source = sources,
target = targets,
value = values
))])
fig.update_layout(
hovermode = 'x',
title="Some Flow",
font=dict(size = 10, color = 'white'),
plot_bgcolor='black',
paper_bgcolor='black'
)
fig.show()
and produces almost what I want although the In-nodes overlapp and the sorting of link is inconsistent.
How can I influence this behaviour?
I found this question in my test today, I have been trying to find correct answer for this but failing to do so.
Question is:
Imagine we have range of page numbers lets say 0, 100. When we click on page lets say 15, we only what to show 10 pages on UI i.e. from page 10 to 20
more example input: 50 output: returns list
[46,47,48,49,50,51,52,53,54,55]
input: 15
output: returns list
[11,12,13,14,15,16,17,18,19,20]
also list should include first page and last page i.e. 0 and 50
so the actual output would be for first example
[0,46,47,48,49,50,51,52,53,54,55,100]
Below is what I have tried
def get_thread_page_num(num, max_page_num):
# Returns 10 numbers dynamically
new_lst =[1,50]
# default list
# defult_lst = [1,2,3,4,5,6,7,8,9,10]
num -4 > 0
num+5 <max_page_num
i = 10
m = 4
p = 5
while i != 0:
if num-1 >0 and m !=0:
new_lst.append(num-m)
i=i-1
m = m-1
elif num+1<max_page_num and p != 0:
new_lst.append(num+p)
i=i-1
p = p-1
print(sorted(new_lst))
get_thread_page_num(9, 50)
In your code m and p starts with value 4 and 5 respectively. In every iteration, either of them decreases by 1. So, after 9 iteration both of them are 0 and new_lst contains 9 elements. Also i becomes 10-9 = 1.
But i never becomes 0 and the loop becomes infinite.
You can try below code instead. Please refer to the comments.
def get_thread_page_num(num, max_page_num):
# low and high denotes the low and high end of the list
# where middle element is num
low = max(0, num - 4)
high = min(num + 5, max_page_num)
lst = []
if max_page_num < 9:
# 10 element list is not possible
return lst
# In case high is same as max, just make the list as
# high-9, high -8, ..., high
if high == max_page_num:
lst = list(range(max(0, high - 9), high + 1))
else:
# Just create a list starting from low like -
# low, low + 1, ..., low + 9
lst = list(range(low, low+10))
# Add 0 and max if not already present
if 0 not in lst:
lst.append(0)
if max_page_num not in lst:
lst.append(max_page_num)
# return sorted lst
return sorted(lst)
Call to get_thread_page_num():
print(get_thread_page_num(15, 50))
print(get_thread_page_num(0, 50))
print(get_thread_page_num(2, 50))
print(get_thread_page_num(50, 50))
print(get_thread_page_num(43, 50))
Output:
[0, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 50]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 50]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 50]
[0, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50]
[0, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 50]
These codes produce a chart
import numpy as np
import matplotlib.pyplot as plt
N = 5
menMeans = (20, 35, 30, 35, 27)
womenMeans = (25, 32, 34, 20, 25)
menStd = (2, 3, 4, 1, 2)
womenStd = (3, 5, 2, 3, 3)
ind = np.arange(N) # the x locations for the groups
width = 0.35 # the width of the bars: can also be len(x) sequence
p1 = plt.bar(ind, menMeans, width, yerr=menStd)
p2 = plt.bar(ind, womenMeans, width,
bottom=menMeans, yerr=womenStd)
plt.ylabel('Scores')
plt.title('Scores by group and gender')
plt.xticks(ind, ('G1', 'G2', 'G3', 'G4', 'G5'))
plt.yticks(np.arange(0, 81, 10))
plt.legend((p1[0], p2[0]), ('Men', 'Women'))
Jupyter notebook automatically print the chart, even I didn't call plt.show(). I don't want to show the chart in the same cell with the code but the next cell by running a really short code such as plt.show(). In order to keep the cell as concise as possible.
Just enclose all your plot-related statements inside a function called plot_and_show(). Then you can call the function when you are ready.
import matplotlib.pyplot as plt
import numpy as np
N = 5
menMeans = (20, 35, 30, 35, 27)
womenMeans = (25, 32, 34, 20, 25)
menStd = (2, 3, 4, 1, 2)
womenStd = (3, 5, 2, 3, 3)
ind = np.arange(N) # the x locations for the groups
width = 0.35 # the width of the bars: can also be len(x) sequence
def plot_and_show():
p1 = plt.bar(ind, menMeans, width, yerr=menStd)
p2 = plt.bar(ind, womenMeans, width,
bottom=menMeans, yerr=womenStd)
plt.ylabel('Scores')
plt.title('Scores by group and gender')
plt.xticks(ind, ('G1', 'G2', 'G3', 'G4', 'G5'))
plt.yticks(np.arange(0, 81, 10))
plt.legend((p1[0], p2[0]), ('Men', 'Women'))
plot_and_show()
When using a list, I saw that I cannot add or subtract the sample I took from the list. For example:
import random
x = random.sample ((1 ,2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13), k=1 )
print(x + 1)
Why I can’t add into the list I created and how can I get around that issue?
If you want to increase the value of every item in a list, you can do like:
import random
x = random.sample ((1 ,2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13), k=3 )
print(x)
for index in range(len(x)):
x[index] = x[index] +1
print(x)
In your case, if k is always 1, you can simply like:
import random
x = random.sample ((1 ,2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13), k=1 )
print(x)
x[0] = x[0] + 1
print(x)
The reason you can't concatenate is because the type random.sample is returning is a list of size k=1. If you want to be returning an element of your sequence and add to it, you should be using random.choice. It should read something along the lines of:
import random
x = random.choice((1,2,3,4,5,6,7,8,9,10,11,12,13))
print(x+1)
I'm having some trouble with tkinter's create_text. I'm trying to iterate through a list and have create_text output each item in the list one by one. I can't figure this out, as every time I've tried, it does not work the way I want it to. Here's some code that exemplifies the issue:
class GUI(Frame):
def __init__(self, master):
self.test_list = [1, 2, 3, 4, 5, 6, 7, 8, 9]
self.c = Canvas(master, width=300, height=300)
self.c.pack()
for items in self.test_list:
items = items
for i in range(0, 300, 100):
for j in range(0, 300, 100):
self.c.create_text(j + 25, i + 20, text=items)
root = Tk()
root.title("Test")
root.geometry("300x300")
GUI(root)
mainloop()
Thank you and I appreciate the help.
Your code had severe indentation problems.
Further, you did not call mainloop on any object.
Then, the position of the objects on the canvas was outside the visible window:
I fixed the code so it runs, and displays something on the canvas; from there, you can modify it to suit your needs.
import tkinter as tk
class GUI(tk.Frame):
def __init__(self, master):
self.test_list = [1, 2, 3, 4, 5, 6, 7, 8, 9]
self.c = tk.Canvas(master, width=300, height=300)
self.c.pack()
for idx, elt in enumerate(self.test_list):
row = (idx // 3 + 5) * 20
col = (idx % 3 + 5) * 20
self.c.create_text(row, col, text=elt)
if __name__ == '__main__':
root = tk.Tk()
root.title("Test")
root.geometry("300x300")
GUI(root)
root.mainloop()
This has two outer loops.
# iterates, items == 9 now
for items in self.test_list:
items = items
# uses 9 over and over
for i in range(0, 300, 100):
for j in range(0, 300, 100):
self.c.create_text(j + 25, i + 20, text=items)
Maybe try this instead.
for items in self.test_list:
for i in range(0, 300, 100):
for j in range(0, 300, 100):
self.c.create_text(j + 25, i + 20, text=items)