Py.Cafe

mwcraig/

solara-global-temp-analysis

Temperature Data Visualization and Smoothing Dashboard with Solara

DocsPricing
  • app.py
  • land-ocean-temp-index.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
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
137
138
139
140
141
142
143
# AUTOGENERATED! DO NOT EDIT! File to edit: ../04c_solara.ipynb.

# %% auto 0
__all__ = ['DATA_DIR', 'DATA_FILE', 'original_df', 'year_range_input', 'year_range', 'window_size', 'polynomial_order',
           'selected_df', 'controls', 'check_poly_order', 'selected_data', 'draw_plot', 'Page']

# %% ../04c_solara.ipynb 2
from pathlib import Path

import pandas as pd
import solara
from ipydatagrid import DataGrid
from matplotlib.figure import Figure
from matplotlib import pyplot as plt
from scipy.signal import savgol_filter


# %% ../04c_solara.ipynb 4
DATA_DIR = '.'
DATA_FILE = 'land-ocean-temp-index.csv'

original_df = pd.read_csv(Path(DATA_DIR) / DATA_FILE, escapechar='#')
year_range_input = (min(original_df["Year"]), max(original_df["Year"]))

# %% ../04c_solara.ipynb 7
year_range = solara.reactive(year_range_input)
window_size = solara.reactive(2)
polynomial_order = solara.reactive(1)


# %% ../04c_solara.ipynb 10
# Here we define our own component, called controls. A component can take arguments, 
# though this one does not.
@solara.component
# Changed something
def controls():
    """
    This panel contains the year_range, window_size and polynomial_order controls.
    """
    # solar.Column() is another component defined in solara itself. Everything in the 
    # with block is arranged in a column.
    with solara.Column() as crtl:
        # SliderRangeInt is another solara component
        solara.SliderRangeInt(
            "Range of years", 
            # The line below is key -- it connects the slider to the reactive variable year_range
            value=year_range, 
            min=year_range_input[0],
            max=year_range_input[1],
        )
    
        solara.SliderInt(
            "Window size",
            # Link this slider to window_size
            value=window_size,
            min=2,
            max=100
        )
        solara.SliderInt(
            "Polynomial order",
            # Link this slider to polynomial_order
            value=polynomial_order,
            min=1,
            max=10
        )
    # If there is a single displayable component in the function then solara will display that,
    # otherwise it renders the return value.
    return crtl



# %% ../04c_solara.ipynb 12
# Registering as a component ensures this is called when either reactive variable's 
# value changes.
@solara.component
def check_poly_order():
    if polynomial_order.value > 10 or polynomial_order.value >= window_size.value:
        polynomial_order.value = min(window_size.value - 1, 10)

# %% ../04c_solara.ipynb 15
selected_df = solara.reactive(original_df.copy())

# %% ../04c_solara.ipynb 17
@solara.component 
def selected_data():
    """
    This component only updates the selected data. Since selected_df is a reactive 
    variable, any component which 1) uses selected_df and 2) is rendered in a UI component
    will automatically be updated.
    """
    original_df['Smoothed Data'] = savgol_filter(original_df['Temperature'],
                                                 window_size.value,
                                                 polynomial_order.value).round(decimals=3)
    selected_df.value = original_df[(original_df['Year'] >= year_range.value[0])
                               & (original_df['Year'] <= year_range.value[1])]


# %% ../04c_solara.ipynb 19
@solara.component
def draw_plot():
    plt.xlabel('Year')
    plt.ylabel('Temperature Anomalies over Land w.r.t. 1951-80 (˚C)')
    plt.title('Global Annual Mean Surface Air Temperature Change')

    plt.plot(selected_df.value['Year'], selected_df.value['Temperature'], label='Raw Data')
    plt.plot(selected_df.value['Year'], selected_df.value['Smoothed Data'], label='Smoothed Data')
    plt.legend()
    plt.show()
    plt.close()


# %% ../04c_solara.ipynb 21
@solara.component
def Page():
    # These first two components are called here so that solara knows it should call them 
    # when changes occur in any of the reactive variables used in those components.
    check_poly_order()
    selected_data()
    
    # We make a row, which will end up with two columns 
    with solara.Row():
        # Here we define the left column and restrict its width to 500px.
        with solara.Column(style=dict(width="500px")):
            # Get some extra space at the top...
            solara.Text("\n\n")
            # Here we use the controls component we defined above.
            controls()
        # Make column 2 with the data and graph. This column will use whatever space
        # is available that the first column doesn't use.
        with solara.Column():
            # Display the data. The Details component is a collapsible component sort of like
            # an accordian. Its child is an ipydatagrid.DataGrid, like we used previously.
            # There is another option built in to solara called solara.DataFrame with similar 
            # functionality.
            solara.Details(
                summary="Click to show data",
                children=[DataGrid(selected_df.value)]
            )
            # This draws the plot. Solara undestands that this needs to be redrawn whenever selected_df
            # changes.
            draw_plot()