-
Notifications
You must be signed in to change notification settings - Fork 146
/
scatter.py
136 lines (114 loc) · 5.3 KB
/
scatter.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
import pathlib
import sys
from typing import Optional, cast
import pandas as pd
import solara
import solara.express as solara_px # similar to plotly express, but comes with cross filters
import solara.lab
from solara.components.columns import Columns
from solara.components.file_drop import FileDrop
github_url = solara.util.github_url(__file__)
if sys.platform != "emscripten":
pycafe_url = solara.util.pycafe_url(path=pathlib.Path(__file__), requirements=["pandas", "plotly"])
else:
pycafe_url = None
df_sample = pd.read_csv("https://raw.githubusercontent.com/plotly/datasets/master/gapminderDataFiveYear.csv")
class State:
size_max = solara.reactive(40.0)
size = solara.reactive(cast(Optional[str], None))
color = solara.reactive(cast(Optional[str], None))
x = solara.reactive(cast(Optional[str], None))
y = solara.reactive(cast(Optional[str], None))
logx = solara.reactive(False)
logy = solara.reactive(False)
df = solara.reactive(cast(Optional[pd.DataFrame], None))
@staticmethod
def load_sample():
State.x.value = "gdpPercap"
State.y.value = "lifeExp"
State.size.value = "pop"
State.color.value = "continent"
State.logx.value = True
State.df.value = df_sample
@staticmethod
def load_from_file(file):
df = pd.read_csv(file["file_obj"])
State.x.value = str(df.columns[0])
State.y.value = str(df.columns[1])
State.size.value = str(df.columns[2])
State.color.value = str(df.columns[3])
State.df.value = df
@staticmethod
def reset():
State.df.value = None
@solara.component
def Page():
df = State.df.value
dark_effective = solara.lab.use_dark_effective()
# the .scatter will set this cross filter
filter, _set_filter = solara.use_cross_filter(id(df))
# only apply the filter if the filter or dataframe changes
def filter_df():
if filter is not None and df is not None:
return df.loc[filter]
dff = solara.use_memo(filter_df, dependencies=[df, filter])
with solara.AppBar():
solara.lab.ThemeToggle()
with solara.Sidebar():
with solara.Card("Controls", margin=0, elevation=0):
with solara.Column():
with solara.Row():
solara.Button("Sample dataset", color="primary", text=True, outlined=True, on_click=State.load_sample, disabled=df is not None)
solara.Button("Clear dataset", color="primary", text=True, outlined=True, on_click=State.reset)
if sys.platform != "emscripten":
FileDrop(on_file=State.load_from_file, on_total_progress=lambda *args: None, label="Drag file here")
else:
solara.Info("File upload not supported in this environment")
if df is not None:
solara.SliderFloat(label="Size", value=State.size_max, min=1, max=100)
solara.Checkbox(label="Log x", value=State.logx)
solara.Checkbox(label="Log y", value=State.logy)
columns = list(map(str, df.columns))
solara.Select("Column x", values=columns, value=State.x)
solara.Select("Column y", values=columns, value=State.y)
solara.Select("Size", values=columns, value=State.size)
solara.Select("Color", values=columns, value=State.color)
if filter is None:
solara.Info("If you select points in the scatter plot, you can download the points here.")
else:
def get_data():
return dff.to_csv(index=False)
solara.FileDownload(get_data, label=f"Download {len(dff):,} selected points", filename="selected.csv")
if df is not None:
with Columns(widths=[2, 4]):
if State.x.value and State.y.value:
solara_px.scatter(
df,
State.x.value,
State.y.value,
size=State.size.value,
color=State.color.value,
size_max=State.size_max.value,
log_x=State.logx.value,
log_y=State.logy.value,
template="plotly_dark" if dark_effective else "plotly_white",
)
else:
solara.Warning("Select x and y columns")
else:
solara.Info("No data loaded, click on the sample dataset button to load a sample dataset, or upload a file.")
with solara.Column(style={"max-width": "400px"}):
solara.Button(label="View source", icon_name="mdi-github-circle", attributes={"href": github_url, "target": "_blank"}, text=True, outlined=True)
if sys.platform != "emscripten":
solara.Button(
label="Edit this example live on py.cafe",
icon_name="mdi-coffee-to-go-outline",
attributes={"href": pycafe_url, "target": "_blank"},
text=True,
outlined=True,
)
@solara.component
def Layout(children):
route, routes = solara.use_route()
dark_effective = solara.lab.use_dark_effective()
return solara.AppLayout(children=children, toolbar_dark=dark_effective, color=None) # if dark_effective else "primary")