Semplici Sondaggi con Streamlit

Streamlit Surveys

I componenti dell’interfaccia utente di Streamlit rendono facile la costruzione di semplici sondaggi

Foto di Nguyen Dang Hoang Nhu su Unsplash
  • Cosa pensi del futuro dell’AI, dovrebbe essere regolamentata, creerà nuovi posti di lavoro o li distruggerà?
  • Come pensi che il cambiamento climatico influenzerà il tuo stile di vita?
  • Credi che ci sia vita aliena nell’universo?
  • Quale è il tuo linguaggio di programmazione preferito per la scienza dei dati?

A volte usiamo i dati di altre persone per creare una storia, altre volte dobbiamo creare i nostri dati e quindi dobbiamo raccoglierli. Questo potrebbe essere un sondaggio o un registro di risultati sperimentali, ma dobbiamo presentare domande e dobbiamo registrare i dati risultanti.

Certo, ci sono servizi che lo faranno per te (a volte a pagamento ma ci sono spesso opzioni gratuite, anche). Oppure potresti rimanere fedele al metodo provato e testato del clipboard e della matita.

Ma creare un semplice sondaggio è piuttosto facile se sei un utente di Streamlit.

Memorizzazione dei dati

C’è però un piccolo problema. I componenti dell’interfaccia utente di Streamlit sono ottimi e facili da usare, ma non ci sono metodi di archiviazione dati integrati. Potresti semplicemente archiviare i dati in un file di testo o un database SQLite e funzionerebbe bene ma solo per un’app locale.

Se provi a distribuire quell’app in Streamlit Cloud, scoprirai che tutti i dati che crei scompaiono.

È ovvio se ci pensi.

Quando avvii un’app Streamlit Cloud, copia i file sorgente da Github, compresi i file di dati o i database, ma quando lasci l’app, non viene scritto nulla. Quindi quando avvii di nuovo l’app, parti da zero. Qualsiasi dato che hai raccolto e archiviato dura solo finché l’app è in esecuzione. Quando lasci l’app, quei dati vengono persi.

Non è una grande funzionalità per un’app di sondaggio.

Le persone di Streamlit hanno ovviamente pensato a questo e hanno suggerito soluzioni nella loro documentazione (vedi la sezione tutorial nella ‘Knowledge Base’). Per lo più, queste soluzioni riguardano la connessione a server di database che eseguono vari database come MySQL, Microsoft SQL Server, ecc. ma mostrano anche come utilizzare Streamlit con servizi basati su cloud come Amazon S3, MongoDB e Google Cloud Storage.

C’è anche Databutton che è un ambiente di sviluppo online completo per Streamlit che presenta, tra le altre cose, il deployment con un solo clic, la codifica supportata dall’AI e, comodamente, l’archiviazione dei dati come parte dell’ambiente di sviluppo e deployment.

Per ora, ci concentreremo sulla parte di sondaggio e tratteremo l’archiviazione separatamente. In questa app, useremo solo un file locale per archiviare i dati ma per rendere la vita più facile in futuro, metteremo tutte le operazioni di file in una libreria. In questo modo, se vogliamo passare a un’altra piattaforma, dobbiamo semplicemente riscrivere la libreria. Quindi, ricorda che la nostra app iniziale non è progettata per essere distribuita ma per funzionare su una macchina locale.

Creazione di un sondaggio in Streamlit

Streamlit fornisce una buona selezione di componenti dell’interfaccia utente che possono essere utilizzati per creare, presentare e analizzare i dati dei sondaggi. In particolare, faremo uso di gruppi di pulsanti radio per implementare domande a risposta multipla e del dataframe modificabile per visualizzare e modificare il questionario stesso.

Domanda a risposta multipla utilizzando i pulsanti radio di Streamlit - screenshot dell'autore

Pensiamo a presentazioni più sofisticate o a tipi di domande diversi, in seguito – per il momento, lo terremo semplice.

Ci sono tre componenti per l’app: l’editor del questionario; la presentazione del sondaggio; e l’analisi/visualizzazione dei risultati. Li ho implementati come pagine in un’app multipagina. (Tutto ciò che significa è che vivono in una cartella chiamata pagine.)

L’editor

Per rappresentare i nostri dati, sia il questionario che i risultati, useremo principalmente i dizionari Python e, in questa versione locale dell’app, li archivieremo come file JSON.

Le domande verranno archiviate in due campi, text, una stringa che contiene il testo della domanda e responses, che è una stringa di risposte a scelta multipla separate da virgole.

Puoi vedere i dati del questionario visualizzati come un componente Streamlit data_editor nello screenshot sotto. E utilizzando questo componente, puoi modificare il questionario direttamente, se lo desideri.

Sopra il dataframe modificabile ci sono un paio di campi: il primo è per la domanda e il secondo è per l’elenco delle possibili risposte. Compila questo modulo e premi il pulsante Aggiungi domanda al questionario e vedrai la nuova domanda apparire nel dataframe.

Come ho detto, puoi anche modificare il dataframe direttamente: fai clic sul campo appropriato per modificare i dati esistenti; fai clic alla sinistra di una riga per selezionarla e utilizza il tasto Elimina per rimuoverla; o fai clic sulla sinistra sotto l’ultima riga per aggiungere una nuova riga.

In entrambi i casi, devi premere Salva modifiche per archiviare i dati.

L'editor del questionario - screenshot dell'autore

Puoi vedere l’implementazione qui sotto.

I programmi Streamlit vengono eseguiti nuovamente ogni volta che c’è un’interazione dell’utente, quindi utilizziamo la funzionalità di sessione di Streamlit per archiviare il questionario in modo che il suo valore sia correttamente mantenuto. Oltre a ciò, è un programma Streamlit piuttosto semplice; presenta due componenti st.text_input() (aggiungi una stringa di risposta predefinita al secondo) seguiti da un st.data_editor() che visualizza il questionario e consente di modificarlo.

La parte finale del programma è dove vengono archiviati i dati. Questo utilizza le routine che ho scritto nella libreria DButils. Sono essenzialmente un involucro intorno alle funzioni di archiviazione di base dei file – come ho detto in precedenza ho implementato lo storage in modo che i programmi possano essere utilizzati con opzioni di archiviazione alternative su una piattaforma diversa.

DButils.get_survey() recupera il questionario archiviato e DButils.save_survey() salva l’intero dataframe nel file.

import streamlit as stimport DButilsst.set_page_config(layout="wide")if 'survey' not in st.session_state:    st.session_state['survey'] = DButils.get_survey()st.title("Editor del questionario")st.write("""Digita il testo della domanda nel campo sottostante e quindi aggiungi una lista di possibili risposte (o puoi lasciare, o modificare, le risposte predefinite).""")# Imposta una risposta predefinita risposta_predefinita = ("Totalmente d'accordo,D'accordo,Né d'accordo né in disaccordo,In disaccordo,Totalmente in disaccordo")st.header("Domanda")q_text = st.text_input("Testo della domanda")q_responses = st.text_input(    "Un elenco separato da virgole di risposte", value=risposta_predefinita)submitted = st.button("Aggiungi domanda al questionario")if submitted:    st.session_state['survey'].append(        {            "text": q_text,            "responses": q_responses,        }    )st.write("Puoi anche modificare direttamente le domande e le risposte nella tabella.")edited_df = st.data_editor(st.session_state['survey'], num_rows="dynamic")save = st.button("Salva modifiche")if save:    DButils.save_survey(edited_df)    st.success(f"Modifiche salvate")

Presenta il questionario

Ogni domanda viene presentata come un gruppo di pulsanti radio.

Presentazione del questionario - screenshot dell'autore

Come puoi vedere qui sotto, iteriamo attraverso il questionario, estrarre il campo text per la richiesta e dividere il campo responses nelle risposte separate per visualizzare il gruppo di pulsanti.

import pandas as pdimport streamlit as stimport DButilsst.info("## Seleziona la risposta per ogni domanda e quindi fai clic su 'Invia'")questions = DButils.get_survey()responses = {}for q in questions:    response = st.radio(label=q['text'], options=q['responses'].split(","))    responses[q['text']] = response.strip()if st.button("Invia"):    entry = responses    DButils.append_results(entry)    st.write("Aggiornato")

L’insieme completo dei dati registrati viene quindi aggiunto alle risposte memorizzate con DButils.update().

Presentare i risultati

La pagina dei risultati è divisa in 3 sezioni: la prima mostra i risultati come una tabella dati che può essere scaricata come file CSV.

Presentazione dei risultati 1 — screenshot dell'autore

La seconda parte è una panoramica grafica dell’intero sondaggio. Il grafico a barre è creato con Plotly Express.

Presentazione dei risultati 2 — screenshot dell'autore

E l’ultima parte consente all’utente di selezionare i risultati per ogni domanda che vengono visualizzati come un grafico a barre (anche questo con Plotly).

Presentazione dei risultati 3 — screenshot dell'autore

Il codice per questo è di seguito. Utilizziamo DButils.get_results() per caricare il dataframe dei risultati e quindi visualizzarlo come un st.dataframe() (non modificabile, naturalmente!) e aggiungiamo un pulsante di download che salverà i dati sul tuo computer locale come file CSV.

In seguito, c’è un grafico a barre dell’intera risposta dei dati (colorata per ogni domanda). E poiché questo non è necessariamente la cosa più facile da leggere, segue un gruppo di pulsanti radio che ti consente di scegliere una domanda specifica su cui concentrarti. I grafici a barre per ogni domanda vengono disegnati in anticipo, in un ciclo, e quello appropriato viene visualizzato per il pulsante radio selezionato.

import streamlit as stimport plotly.express as pximport pandas as pdimport DButilsst.set_page_config(layout="wide")st.info("## Ecco i risultati:")st.write("I risultati sono presentati come un dataframe.")# Leggi i dati dallo store dei dati del pulsante results = DButils.get_results()st.dataframe(results, use_container_width=True)df = pd.DataFrame(results)st.download_button(    label="Scarica i dati come CSV",    data=df.to_csv().encode("utf-8"),    file_name="risultati_sondaggio.csv",    mime="text/csv",)# Traccia un grafico a barre di sintesi fig = px.bar(results, title="Risposte al sondaggio - panoramica")fig.update_xaxes(title_text="Risposta")fig.update_yaxes(title_text="Conteggio")st.plotly_chart(fig)# Crea un array di figure grafiche a barre# una per ogni domanda figures = []for q in df.columns:    fig = px.bar(df[q], title=q)    fig.update_layout(showlegend=False)    fig.update_xaxes(title_text="Risposta")    fig.update_yaxes(title_text="Conteggio")    figures.append(fig)# Scegli quale grafico visualizzare con un insieme di pulsanti radio st.info("### Scegli il grafico per una domanda specifica")f = st.radio("Scegli un grafico", options=df.columns)column_index = df.columns.get_loc(f)st.plotly_chart(figures[column_index])

La libreria DButils

Come puoi vedere di seguito, la libreria DButils ha una serie di funzioni per la lettura, la scrittura e l’aggiornamento di file CSV. Definisce anche costanti per i due file che abbiamo usato sopra.

La libreria è scritta specificamente per un’app locale che utilizza file JSON per memorizzare i dati, ma se volessi portare il tutto su un’altra piattaforma, ci sono solo quattro semplici funzioni da riscrivere e due costanti da definire.

import osimport jsonSURVEY_KEY = "survey.json"RESULTS_KEY = "results.json"# Salva i dati def save_dict(value, key=SURVEY_KEY):    print(f"Salvataggio: {value}")    #return None    out_file = open(key, "w")    json.dump(value,out_file)    out_file.close()def save_results(value):    save_dict(value,RESULTS_KEY)def save_survey(value):    save_dict(value, SURVEY_KEY)# Recupera i dati def retrieve(key):    # il file esiste, leggilo e restituisci un array di dict    if os.path.isfile(key):        in_file = open(key, "r")        result = json.load(in_file)        in_file.close()        return result    else:        # Il file non esiste, restituisce un array di dict vuoto        return []def get_survey(key=SURVEY_KEY):    return retrieve(key)def get_results(key=RESULTS_KEY):    return retrieve(key)# Aggiornamento dei risultati# Potrebbe non essere efficiente, ma è semplice def append_results(value):    results = get_results()    results.append(value)    save_results(results)

Nel mondo reale

Spero che possiate concordare sul fatto che queste semplici routine creino un’applicazione ragionevolmente attraente e vi mostri come Streamlit possa essere utilizzato per creare semplici sondaggi.

Tuttavia, ci sono alcune cose che dovreste considerare se voleste implementare una cosa del genere nel mondo reale.

Ecco alcune cose che potreste voler considerare:

  • Anche se i sondaggi vengono effettuati in forma anonima, potreste voler identificare il rispondente per evitare inserimenti duplicati.
  • Potreste voler includere diversi tipi di domande o presentarle in modi diversi (ad esempio un st.select_slider()).
  • La casualità nella presentazione delle risposte può talvolta evitare di indirizzare il rispondente verso una particolare risposta.
  • In ogni caso, vorreste aggiungere domande demografiche al sondaggio. Anche queste possono essere implementate come domande a risposta multipla, ma i risultati devono essere trattati in modo diverso dagli altri durante l’analisi.

Tuttavia, questo non è un tutorial su come progettare un sondaggio, quindi mi fermo qui.

Grazie per aver letto, spero che lo troviate una guida utile su come affrontare la progettazione di sondaggi in Streamlit. Questa applicazione è deliberatamente molto semplice e lo stoccaggio dei dati funziona solo quando l’applicazione viene distribuita localmente – spero di affrontare queste cose in un prossimo articolo.

Se volete vedere altri miei lavori, date un’occhiata alla mia pagina web.