Py.Cafe

marie-anne/

2025-w4-figurefriday

Selection histogram filters ag-grid and updates dynamic title

DocsPricing
  • app.py
  • 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
# -*- coding: utf-8 -*-
"""
Created on Wed Jan 29 07:25:02 2025

@author: win11
"""
 
from dash import Dash, dcc, html, Input, Output
import dash_ag_grid as dag
import plotly.express as px
import pandas as pd
import plotly.graph_objects as go
import dash_bootstrap_components as dbc



df = pd.read_csv("https://raw.githubusercontent.com/plotly/Figure-Friday/refs/heads/main/2025/week-4/Post45_NEAData_Final.csv")
df['age of writer'] = df.nea_grant_year - df.birth_year

df.sort_values(by='nea_grant_year', inplace=True)

df['teller'] = 1
df['sponsorships'] = df.groupby("nea_person_id")['teller'].cumsum()

dfs = df.groupby('nea_grant_year')['teller'].sum().reset_index()

color_discrete_map = {4: '#072F5F', 3: '#1261A0', 2: '#3895D3', 1: '#58CCED'}

fig = px.histogram(
    df, 
    title='NEA sponsorships granted by year',
    x="nea_grant_year", 
    color='sponsorships',
    color_discrete_map=color_discrete_map,
    labels={'nea_grant_year': 'Year', 'count': 'Total'},
    nbins=60
)

fig.update_xaxes(range=[1966, 2025])
fig.update_layout(
    legend=dict(x=.73, y=.98, title="Number of sponsorships")
)

fig.add_trace(go.Scatter(
    x=dfs['nea_grant_year'], 
    y=dfs['teller'],
    text=dfs['teller'],
    mode='markers+text',
    name="Total number of writers sponsored by NEA",
    textposition='top center',
    textfont=dict(size=12),
    showlegend=True,
    hoverinfo='skip'
))

for tr in fig.select_traces():
    tr['name'] = tr['name'].replace('1', 'First sponsorship')
    tr['name'] = tr['name'].replace('2', 'Second sponsorship')
    tr['name'] = tr['name'].replace('3', 'Third sponsorship')
    tr['name'] = tr['name'].replace('4', 'Fourth sponsorship')


app =Dash(external_stylesheets=[dbc.themes.SUPERHERO])

def filter_grid(filtered_data):
    columnDefs = [
        {"headerName": "Full Name", "field": "full_name_lastfirst"},
        {"headerName": "Gender", "field": "gender"},
        {"headerName": "Year", "field": "nea_grant_year"},
        {"headerName": "Age of writer", "field": "age of writer"},
        {"headerName": "Hometown", "field": "hometown"},
        {"headerName": "Country", "field": "country"},
        {"headerName": "Number of sponsorships", "field": "sponsorships"},
    ]
    
    defaultColDef = {"resizable": False, "sortable": True, "editable": False}
    
    return dag.AgGrid(
        id="custom-component-graph-grid",
        rowData=filtered_data.to_dict("records"),
        columnDefs=columnDefs,
        columnSize="autoSize",
        defaultColDef=defaultColDef,
        dashGridOptions={"pagination": True}
    )






app.layout = dbc.Container([
    dbc.Row(
        dbc.Col( dcc.Graph(id="nea_histogram", figure=fig),)
        ),
    dbc.Row([
        dbc.Col(html.H2(id="title-container", style={'marginTop':'10px','marginBottom':'10px'})  )
        ]),
    dbc.Row([
        dbc.Col(html.Div(id="grid-container") , className='col-md-12')
        ])
    
    
    ])



@app.callback(
    Output("grid-container", "children"),
    Output("title-container", "children"),
    Input("nea_histogram", "clickData")
)
def update_grid(clickData):
    if clickData is None:
        return filter_grid(df), 'All writers'  # Show all data initially
    
    # Extract year and sponsorship count from click event
    clicked_year = clickData["points"][0]["x"]
    clicked_neacount = clickData["points"][0]["curveNumber"] +1  # Adjust if needed

    # Filter dataframe
    filtered_df = df[
        (df["nea_grant_year"] == clicked_year) & 
        (df["sponsorships"] == clicked_neacount)
    ]
    
    #dict to insert the right number of sponsorships in the dynamic title
    writer_text = {1:'first sponsorship', 2: 'second sponsorship', 3:'third sponsorship', 4: 'fourth sponsorship'}
    #dynamic title construction
    title = f"Writers who got their {writer_text[clicked_neacount]} in {clicked_year}"
    
    return filter_grid(filtered_df), title

if __name__ == "__main__":
    app.run(debug=True)