Py.Cafe

Sindhup24/

solara-time-series-analysis

Time Series Analysis with Solara and Altair

DocsPricing
  • app.py
  • merged_river_data.csv
  • requirements.txt
app.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
import altair as alt
import pandas as pd
import solara
from solara.website.utils import apidoc

# Load the river data
file_path = 'merged_river_data.csv'  # Ensure the path is correct on py.cafe
river_data = pd.read_csv(file_path)

# Convert ForecastData from string representation of lists to actual lists
river_data['ForecastData'] = river_data['ForecastData'].apply(eval)

# Convert RiverNumber to string for compatibility
river_data['RiverNumber'] = river_data['RiverNumber'].astype(str)

# Create a map chart using Altair
points = alt.Chart(river_data).mark_circle(
    size=10,
    color='steelblue',
    opacity=0.8
).encode(
    longitude='XCoordinate:Q',
    latitude='YCoordinate:Q',
    tooltip=['RiverNumber', 'Name']
).properties(
    width=800,
    height=400,
    title='Interactive River Flow Forecast Map'
)

@solara.component
def Page():
    click_data, set_click_data = solara.use_state({})
    hover_data, set_hover_data = solara.use_state({})

    def convert_to_dataframe(data):
        if data:
            relevant_keys = ["RiverNumber", "Name", "XCoordinate", "YCoordinate"]
            filtered_data = {key: [data.get(key, '')] for key in relevant_keys}
            return pd.DataFrame(filtered_data)
        return pd.DataFrame()

    def convert_to_forecast_dataframe(data):
        if data:
            river_number = data.get("RiverNumber", "")
            forecast_data = river_data[river_data["RiverNumber"] == river_number]["ForecastData"]
            if not forecast_data.empty:
                return pd.DataFrame({
                    "Time": list(range(len(forecast_data.iloc[0]))),
                    "ForecastData": forecast_data.iloc[0]
                })
        return pd.DataFrame()

    click_df = convert_to_dataframe(click_data)
    forecast_df = convert_to_forecast_dataframe(click_data)

    with solara.Div() as main:
        solara.AltairChart(points, on_click=set_click_data, on_hover=set_hover_data)

        solara.Markdown("### Click data:")
        if not click_df.empty:
            solara.DataFrame(click_df)
        else:
            solara.Markdown("No data")

        solara.Markdown("### Forecast Data:")
        if not forecast_df.empty:
            base = alt.Chart(forecast_df).encode(
                x=alt.X('Time:Q', title='Date'),
                y=alt.Y('ForecastData:Q', title='Discharge, cubic meters/sec')
            )
            
            area = base.mark_area(color='black').encode(
                tooltip=['Time', 'ForecastData']
            )
            
            low_flow = base.mark_line(color='green').encode(
                y=alt.datum(100)  # Assuming low flow threshold is 100 for this example
            )
            
            flood_1_5 = base.mark_line(color='magenta').encode(
                y=alt.datum(1500)  # 1.5 year flood threshold
            )
            
            flood_10 = base.mark_rule(color='red', strokeDash=[5, 5]).encode(
                y=alt.datum(2000)  # 10 year flood threshold
            )
            
            flood_25 = base.mark_rule(color='red').encode(
                y=alt.datum(2500)  # 25 year flood threshold
            )
            
            line_plot = (area + low_flow + flood_1_5 + flood_10 + flood_25).properties(
                width=600,
                height=400,
                title=f'Forecast Data for River {click_data.get("RiverNumber", "")}'
            )

            solara.AltairChart(line_plot)
        else:
            solara.Markdown("No data")

    return main

# This is for documentation purposes
doc = apidoc(solara.AltairChart)  # type: ignore