本記事はStreamlit入門の最終回です。StreamlitはデータをWeb上で簡単に可視化できるようにするライブラリです。各種DBやファイルなどに接続する方法が用意されています。
- Amazon S3
- BigQuery
- Firestore
- Google Cloud Storage
- Microsoft SQL Server
- Postgres
- MySQL
- …
https://docs.streamlit.io/knowledge-base/tutorials/databases
本記事はFuture Coders独自教材からの抜粋です。
Streamlit入門 – 10)データベース BigQuery, GCS等 目次
Firebase(Firestore)
https://blog.streamlit.io/streamlit-firestore/
準備 (アプリの作成とデプロイ)
GitHubにリポジトリを作成
任意の名前のリポジトリを作成して、ローカルにCloneしてください。
以下はリポジトリのURLからクローン(ローカルにコピー)する例です。
C:\streamlit\samples>git clone https://github.com/future-coders-net/streamlit-firestore.git
Cloning into 'streamlit-firestore'...
remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 3 (delta 0), pack-reused 0
Receiving objects: 100% (3/3), done.
もしくはローカルでリポジトリを作成して、GitHubと紐づけて構いません。以降の手順は作成したリポジトリのフォルダをカレントディレクトリとして実行してください。
ローカルで実行
requirements.txtを以下のように作成してフォルダに追加します。
streamlit
google-cloud-firestore
このrequirements.txtファイルには、このアプリで使用するPythonのモジュールを列挙しておきます。streamlit.ioでアプリは実行されますが、そのサーバではrequirements.txtを参考にして必要なモジュールをインストールします。
次はアプリ本体です。以下のファイル(streamlit_app.py)をフォルダに追加します。
import streamlit as st
st.header('Hello 🌎!')
if st.button('Balloons?'):
st.balloons()
以下のコマンドを実行してモジュールを追加します。このコマンドを実行するとrequirements.txtに列挙されたモジュールをまとめてインストールすることができます。
pip install -r requirements.txt
以下のコマンドを実行して、ローカル環境でアプリが実行されることを確認します。
C:\streamlit\samples\streamlit-firestore>streamlit run streamlit_app.py
You can now view your Streamlit app in your browser.
Local URL: http://localhost:8501
Network URL: http://192.168.11.11:8501
ブラウザが起動して以下の画面が表示されます。
コマンドプロンプトでCtrl+Cキーでサーバを停止できます。
デプロイ
ここまではローカルのPCでアプリを実行しただけです。他の人がアプリにアクセスすることは出来ません。だれもがいつでもアプリを実行できるように、streamlit.io上に公開してゆきましょう。このような作業をデプロイと呼びます。
まず、Gitコマンドでadd, commit, pushを行います。
C:\samples\streamlit-firestore>git add .
C:\samples\streamlit-firestore>git commit -m "initial commit"
[main 4be7ed1] initial commit
2 files changed, 7 insertions(+)
create mode 100644 requirements.txt
create mode 100644 streamlit_app.py
C:\samples\streamlit-firestore>git push
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 16 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (4/4), 457 bytes | 457.00 KiB/s, done.
Total 4 (delta 0), reused 0 (delta 0), pack-reused 0
To https://github.com/future-coders-net/streamlit-firestore.git
fae2701..4be7ed1 main -> main
ブラウザで https://share.streamlit.io/ を開き、アプリの追加を行います。
アカウントがない場合は作成を促されます。サインインを促された場合も、画面の手順にそって進めてください。
新規に作成するアプリの情報を記述します。リポジトリとブランチ、最初に実行するファイルを指定します。
Deployボタンを押下すると、デプロイが開始され、作成したアプリが公開されます。
Firestoreのプロジェクト作成
以下のURLからFirebaseのプロジェクトを作成して、firestoreを有効化します。プロジェクト作成直後は有効化が失敗することがあります。作成後しばらくたってから有効化するとよいでしょう。
https://console.firebase.google.com/
テストモードで開始し、ロケーションを選択し、有効にするボタンを押下します。
Firestoreへのデータのアップロード
Firestoreでは、1件のデータを1つのドキュメント(JSONファイルのようなイメージ)として管理し、それらのドキュメントをCollectionでまとめます。Collectionがフォルダ、各ドキュメントがファイル、のようなイメージです。
Firebaseのプロジェクト設定から、サービスアカウントタブを選択しい、「新しい秘密鍵を生成」ボタンを押下して、サービスアカウントキーをダウンロードします。serviceAccountKey.jsonというファイル名にして、Streamlitのプロジェクトフォルダに格納してください。
通常はセキュリティの観点から秘密鍵をGithubのリポジトリにいれることはしません。鍵をリポジトリに含めずに動作させる手順に関しては後述します。とりあえずは動作確認のためリポジトリに鍵を含めることにします。
firebase_adminパッケージをPIPコマンドでインストールしてください。また、今回はIRIS(アヤメ)のデータをFirestoreに保存したいので、seabornもインストールします。これらのモジュールはサーバ上でStreamlitを実行するときに使用するものではないので、requirements.txtに含める必要はありません。
pip install firebase_admin
pip install seaborn
以下のプログラムを実行すると、アヤメのデータがFirestoreにアップロードされます。
import firebase_admin
from firebase_admin import credentials
from firebase_admin import firestore
cred = credentials.Certificate("serviceAccountKey.json")
firebase_admin.initialize_app(cred)
db = firestore.client()
import seaborn as sns
df = sns.load_dataset("iris")
for index, row in df.iterrows():
db.collection("iris").add(row.to_dict())
firebase_adminはPythonからFirebaseを操作するためのモジュールです。credntials.Certificateでダウンロードした秘密鍵から認証情報を取得して、アプリを初期化します。パスワードでログインしているようなイメージです。firestore.client()でデータベースオブジェクトを取得し、db.collection(コレクション名)でコレクションを参照し、そこにaddメソッドでドキュメントを追加しています。
実行すると以下のようにfirestoreにデータが保存されます。
以下のプログラムはfirestoreからirisコレクションに含まれるドキュメントを取得して出力します。動作確認をしておくとよいでしょう。
from google.cloud import firestore
db = firestore.Client.from_service_account_json("serviceAccountKey.json")
docs = db.collection("iris").stream()
for d in docs:
print(d.to_dict())
Irisアプリ
以下は、FirestoreからダウンロードしたデータからDataFrameを作成し、そのDataFrameとグラフを描画するStreamlitのアプリです。
import streamlit as st
import pandas as pd
from google.cloud import firestore
db = firestore.Client.from_service_account_json("serviceAccountKey.json")
docs = db.collection("iris").stream()
data = []
for d in docs:
data.append(d.to_dict())
spieces = st.sidebar.selectbox("spieces", ["setosa","versicolor","virginica"])
df = pd.DataFrame(data)
df = df[df["species"] == spieces].reset_index(drop=True)
st.write(df)
st.line_chart(df, x="sepal_length", y="sepal_width")
以下のコマンドを実行するとローカルでサーバが起動します。
streamlit run streamlit_iris.py
サイドバーでアヤメの種類を選択すると、選ばれた種類だけを抽出したデータ(表とグラフ)が描画されます。
以下のコマンドを実行して、コミット・プッシュを行います。これでGithubにファイルがアップロードされます。
git add .
git commit -m "iris"
git push
以下のリンクからアプリを”New app”で作成して、今回作成したリポジトリ、ファイルを選択します。
クラウド上でも同じアプリが実行されていることが確認できます。
Bookmarkアプリ
以下のファイルをローカルで実行してください。タイトルとURLを入力してボタンを押下することで、その内容がFirestoreに保存されることも確認してください。
import streamlit as st
from google.cloud import firestore
db = firestore.Client.from_service_account_json("serviceAccountkey.json")
# Streamlit widgets to let a user create a new post
title = st.text_input("Post title")
url = st.text_input("Post url")
submit = st.button("Submit new post")
# Once the user has submitted, upload it to the database
if title and url and submit:
doc_ref = db.collection("posts").document(title)
doc_ref.set({
"title": title,
"url": url
})
# And then render each post, using some light Markdown
posts_ref = db.collection("posts")
for doc in posts_ref.stream():
post = doc.to_dict()
title = post["title"]
url = post["url"]
st.subheader(f"Post: {title}")
st.write(f":link: [{url}]({url})")
JSON秘密鍵をtomlファイルへ変換
“serviceAccountkey.json”をGitHubに保存するのは望ましくありません。その回避方法について説明します。まずJSONの秘密鍵をtoml形式に変換します。tomlとは最小限の設定ファイルです。
import toml
output_file = ".streamlit/secrets.toml"
with open("serviceAccountKey.json") as json_file:
json_text = json_file.read()
config = {"textkey": json_text}
toml_config = toml.dumps(config)
with open(output_file, "w") as target:
target.write(toml_config)
“.streamlit”というフォルダを作成してから、以下のコマンドを実行してください。
python key-to-toml.py
.streamlitフォルダにsecrets.tomlというファイルが作成されていることを確認します。
元のファイルにあった以下の部分を置き換えます。
db = firestore.Client.from_service_account_json("serviceAccountkey.json")
置き換える内容は以下の通りです。
from google.oauth2 import service_account
import json
key_dict = json.loads(st.secrets["textkey"])
creds = service_account.Credentials.from_service_account_info(key_dict)
db = firestore.Client(credentials=creds, project="streamlit-test-10fb6")
firestore.Clientのproject引数には正式なプロジェクトIDを指定します。プロジェクトのIDはFirebaseコンソールの設定から調べます。
.gitignoreファイルを作成して以下の記述を追加します。
secrets.toml
serviceAccountkey.json
git add .
git commit -m "remove secrets"
git push
serviceAccountKey.jsonを削除してPushするとGithubからserviceAccountkey.jsonがなくなります。
Secretの設定
tomlファイルに変換しましたが、このファイルもGithubにはアップロードされていません。このままでは何も認証情報がない状態となるので、Firestoreへの読み書きが失敗します。そこで、今回変換したtomlの値をstreamlitのダッシュボードから設定します。
以下のようにtomlファイルの内容をペーストします。
反映されるまでに少し時間がかかります。多少待ってからアプリにアクセスすると、firestoreへの読み書きができるようになっていることが確認できます。
Firebase (Secrets)
Firestoreへのアクセスでは、秘密鍵をsecretsに登録しました。WebAPIにアクセスしたり、データベースに接続したり、と認証情報(アカウントとパスワード)が必要になることは少なくありません。しかし、これらの情報をリポジトリにふくめることはセキュリティの観点から望ましくありません。Streamlitではこのような状況に対処するためSecretsという仕組みが用意されています。
以下は公式のチュートリアルです。
https://blog.streamlit.io/secrets-in-sharing-apps/
SecretsにはTOML形式で情報を格納します。以下はTOMLファイルの例です。
# Everything in this section will be available as an environment variable
db_username = "Jane"
db_password = "12345qwerty"
# You can also add other sections if you like.
[my_cool_secrets]
things_i_like = ["Streamlit", "Python"]
で始まる行はコメントです。変数名 = 値 のように記述します。
アプリからは以下のようにアクセスします。
import streamlit as st
st.write("DB username:", st.secrets["db_username"])
st.write("DB password:", st.secrets["db_password"])
Secretsの設定
実際の値はアプリの設定画面(share.streamlit.io)から行います。アプリの横にあるメニューからSettingsを選択します。
Secretsのタブで値を設定します。
ローカルでの設定
.streamlitというフォルダを作成し、そのなかにsecrets.tomlファイルを配置します。その中にTOML形式で値を記述します。このようにすればローカルで実行したときには、このファイルから値を取得しにゆきます。
演習
ex-database1.py
secretsの動作を確認してください。以下の例のようにdb_username, db_passwordの値をsecretsから取得して表示します。ローカル、クライド、両方の環境で動作することを確認してください。
ex-database2.py
seabornのデータセットからPenguinを取得して、firestoreにアップロードします。Firestoreからデータを取得して表示するStreamlitのアプリを公開してください。
以下はmatplotlibのpyplotを使って散布図を描画した例です。
“`python
import matplotlib.pyplot as plt
…
fig, ax = plt.subplots()
ax.scatter(x=df[“bill_length_mm”], y=df[“body_mass_g”])
st.pyplot(fig)
BigQuery
以下BigQueryに接続するための公式チュートリアルです。
https://docs.streamlit.io/knowledge-base/tutorials/databases/bigquery
BigQueryの準備
1. APIの有効化
BigQueryに接続するには、BigQuery側でAPIを有効化する必要があります。対象となるプロジェクトが選択されていることに注意してください。
https://console.cloud.google.com/apis/dashboard
2. サービスアカウントの作成
データベースにアクセスするときや、特定のサービスを利用するときに、アカウントとパスワードで認証することが一般的です。今回はStreamlitというサービスからBigQueryへアクセスすることになります。GCP (Google Cloud Platform)では、このようなサービスからのアクセスと、人によるアクセスを区別できるようになっています。今回はStreamlitというサービスからアクセスするので、そのためのアカウントを作成します。 アカウントを作成するだけではなく、「このアカウントは何をすることができるのか」ということを細かく指定できるようになっています。 次にStreamlitからBigQueryにアクセスするためのViewer permissionを持つサービスアカウントを作成します。以下のURLから、画面を参考に設定を行ってください。
https://console.cloud.google.com/iam-admin/serviceaccounts
Permissionは種類も多く選ぶのが難しいかもしれません。Quick accessでBasicを選び、右のRolesからViewerを選択するとよいでしょう。
3. JSONキーの取得
サービスアカウントが作成されると一覧画面に戻ります。コンテキストメニューからManage Keysを選択し、ADD KEYからCreate new keyを選択し、JSONキーをダウンロードします。
以下のようなキーが保存されていることを確認してください
{
"type": "service_account",
"project_id": "future-coders-1536465074795",
"private_key_id": "7cabb372f25515bfdcfbc41662eab9b28dc44feb",
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDGJJbv6/...
<<中略>>
6KAX2EI1cZaWcAObw=\n-----END PRIVATE KEY-----\n",
"client_email": "streamlit-service@future-coders-1536465074795.iam.gserviceaccount.com",
"client_id": "117191943597618343864",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/streamlit-service%40future-coders-1536465074795.iam.gserviceaccount.com",
"universe_domain": "googleapis.com"
}
4. TOMLキーの設定
TOMLキーの設定 .streamlitフォルダのsecrets.tomlに以下の内容を追加します。実際の値はダウンロードしたファイルから取得してください。
# .streamlit/secrets.toml
[gcp_service_account]
type = "service_account"
project_id = "xxx"
private_key_id = "xxx"
private_key = "xxx"
client_email = "xxx"
client_id = "xxx"
auth_uri = "https://accounts.google.com/o/oauth2/auth"
token_uri = "https://oauth2.googleapis.com/token"
auth_provider_x509_cert_url = "https://www.googleapis.com/oauth2/v1/certs"
client_x509_cert_url = "xxx"
アプリの実装
bigqueryにアクセスするためにはgoogle-cloud-bigqueryモジュールが必要です。以下のコマンドでモジュールをインストールしてください。
pip install google-cloud-bigquery
実際にインストールされたバージョンはpip listコマンドで確認ができます。バージョンを調べて、requirements.txtに以下のような行を追加してください。
google-cloud-bigquery==3.11.4
以下がアプリケーションのプログラムです。
import streamlit as st
from google.oauth2 import service_account
from google.cloud import bigquery
# Create API client.
credentials = service_account.Credentials.from_service_account_info(
st.secrets["gcp_service_account"]
)
client = bigquery.Client(credentials=credentials)
# Perform query.
# Uses st.cache_data to only rerun when the query changes or after 10 min.
@st.cache_data(ttl=600)
def run_query(query):
query_job = client.query(query)
rows_raw = query_job.result()
# Convert to list of dicts. Required for st.cache_data to hash the return value.
rows = [dict(row) for row in rows_raw]
return rows
rows = run_query("SELECT word FROM `bigquery-public-data.samples.shakespeare` LIMIT 10")
# Print results.
st.write("Some wise words from Shakespeare:")
for row in rows:
st.write(✍️ " + row['word'])
@st.cache_data(ttl=600)
と記述しているところがあります。これは関数を実行した結果を600秒(10分)保存しておくという命令です。ページが何回も読み込まれたときに都度クエリを実行されることを避けることができます。
以下のようなクエリを実行しています。これはbigqueryであらかじめ用意されているサンプルデータ(シェークスピア)から先頭の10単語を検索する命令です。
"SELECT word FROM `bigquery-public-data.samples.shakespeare` LIMIT 10"
実行すると以下のように表示されます。
演習
ex-bigquery1.py
BigQueryのbigquery-public-dataからテーブルを選択して、Streamlitで表示してください。
以下のURLからBigQueryを表示し、ADDボタンをクリックします。
https://console.cloud.google.com/bigquery
“Star a project by name”を選択し、bigquery-public-data と入力し、Starボタンをクリックします。
public-dataの一覧がエクスプローラーに表示されます。
クエリを実行してbbc_newsデータセットから20件のニュースを表示してください。
SELECT * FROM `bigquery-public-data.bbc_news.fulltext` LIMIT 20
このニュースのタイトルを箇条書きで10件表示するStreamlitアプリを作成し、ローカルとクラウドの両方で実行してください。expander部品を使って、タイトルにtitleを、本文にbodyを表示するとよいでしょう。
ex-bigquery2.py
BigQueryのpublic dataの別の例です。
以下のクエリを実行すると、国別のCOVID-19による死者数を調べることができます。
SELECT date, deaths, countries_and_territories FROM `bigquery-public-data.covid19_ecdc.covid_19_geographic_distribution_worldwide`
このデータを使って、いくつかの国の死者数のグラフを描画してください。
このテーブルをPythonで処理するためには以下のパッケージをインストールしておく必要があります。
pip install db-dtypes
また、以下のようにto_dataframe()メソッドを使用すると、BigQueryを実行した結果をDataFrameで受け取ることも可能です。
result = client.query(query).to_dataframe()
解答例
ex-bigquery1.py
import streamlit as st
from google.oauth2 import service_account
from google.cloud import bigquery
# Create API client.
credentials = service_account.Credentials.from_service_account_info(
st.secrets["gcp_service_account"]
)
client = bigquery.Client(credentials=credentials)
@st.cache_data(ttl=600)
def run_query(query):
query_job = client.query(query)
rows_raw = query_job.result()
rows = [dict(row) for row in rows_raw]
return rows
rows = run_query("SELECT * FROM `bigquery-public-data.bbc_news.fulltext` LIMIT 10")
# Print results.
st.header("BBC Top 10 news")
for row in rows:
with st.expander(row['title']):
st.write(row['body'])
ex-bigquery2.py
import streamlit as st
from google.oauth2 import service_account
from google.cloud import bigquery
# Create API client.
credentials = service_account.Credentials.from_service_account_info(
st.secrets["gcp_service_account"]
)
client = bigquery.Client(credentials=credentials)
@st.cache_data(ttl=600)
def run_query(query):
result = client.query(query).to_dataframe()
return result
df = run_query("""SELECT date, deaths, countries_and_territories as country
FROM `bigquery-public-data.covid19_ecdc.covid_19_geographic_distribution_worldwide`""")
country = st.sidebar.selectbox("country",
["Japan", "Australia", "Brazil", "China", "United_States_of_America"])
df = df[df["country"] == country]
df = df.reset_index(drop=True)
st.header("COVID-19 numbers")
st.subheader(country)
st.line_chart(df, x="date", y="deaths")
Google Cloud Storage
Google Clous Storage (GCS) はAWSに置けるS3やAzureにおけるBlobのように、ファイルを自由に置くことができるサービスです。ここにCSVやExcelなどのデータを置いておき、Streamlitからそのファイルを取得することも可能です。
以下は公式チュートリアルのURLです。
https://docs.streamlit.io/knowledge-base/tutorials/databases/gcs
GCSの準備
GCSのコンソールからCloud Storageを選択し、バケット(フォルダ)streamlit-bucket-fcを作成し、以下のCSVファイルをアップロードします。バケット名はグローバルで一意である必要があるので、重複のない名前を適当に選んでください。このバケット名はStreamlitアプリの中から指定します。
https://console.cloud.google.com/storage/browser
Location typeは適当でかまいません。今回はテスト目的なのでRegionを選択し、asia-northeast1 (Tokyo)で十分でしょう。他の設定はデフォルトのままで大丈夫です。
バケット作成後にCSVファイルをアップロードします。
Owner,Pet
Mary,dog
John,cat
Robert,bird
APIの有効化
以下のURLからCloud Storage APIを有効化します。
https://console.cloud.google.com/apis/library/storage.googleapis.com
サービスアカウントの作成
以下のURLからプロジェクトを選択し、BigQueryのときと同じようにViewer権限をもつサービスアカウントを作成します。
https://console.cloud.google.com/iam-admin/serviceaccounts
作成後にJSONファイルをダウンロードし、その内容を参考に.streamlit/secrets.tomlファイルを作成します。詳しくはBigQueryの解説を参照してください。
tomlファイルでは設定の先頭に[connections.gcs]を記述する必要があることに注意してください。
[connections.gcs]
type = "service_account"
project_id = "xxx"
private_key_id = "xxx"
private_key = "xxx"
client_email = "xxx"
client_id = "xxx"
auth_uri = "https://accounts.google.com/o/oauth2/auth"
token_uri = "https://oauth2.googleapis.com/token"
auth_provider_x509_cert_url = "https://www.googleapis.com/oauth2/v1/certs"
client_x509_cert_url = "xxx"
アプリの作成
GCS用のモジュールを追加します。
pip install gcsfs
また、ファイルをGCSからダウンロードするためのモジュールを以下のコマンドで追加します。
pip install git+https://github.com/streamlit/files-connection
このモジュールはexperimental_connectionという関数を使用するために使用します。experimental(実験的)という関数名から、将来的には別の関数で置き換えられる可能性があるかもしれません。
インストールされたバージョンはpip listで確認できます。requirements.txtにGCS用のモジュールを追加します。バージョンは指定しない場合最新版が使用されます。ローカルと同じ環境を再現したい場合は記述したほうがよいでしょう。
gcsfs==2023.6.0
git+https://github.com/streamlit/files-connection
以下はStreamlitアプリのソースコードです。readメソッドの最初の引数にバケットへのパスを指定します。自分の作成したバケット名を指定する必要があることに注意してください。
import streamlit as st
from st_files_connection import FilesConnection
# Create connection object and retrieve file contents.
# Specify input format is a csv and to cache the result for 600 seconds.
conn = st.experimental_connection('gcs', type=FilesConnection)
df = conn.read("streamlit-bucket-fc/myfile.csv", input_format="csv", ttl=600)
# Print results.
for row in df.itertuples():
st.write(f"{row.Owner} has a :{row.Pet}:")
実行した結果以下のように表示されました。GCSバケットに表示したCSVの内容から値が表示されています。
演習
ex-gcs1.py
任意のCSVファイルをGCSに配置して、StreamlitアプリからそのCSVファイルを読み取ってデータフレームやグラフを描画してください。例えば、TITANICのデータは以下からダウンロード可能です。CSVにファイルに保存して、GCSにアップロードしてください。
https://github.com/datasciencedojo/datasets/blob/master/titanic.csv
解答例
ex-gcs1.py
import streamlit as st
from st_files_connection import FilesConnection
conn = st.experimental_connection('gcs', type=FilesConnection)
df = conn.read("streamlit-bucket-fc/titanic.csv", input_format="csv", ttl=600)
st.header("TITANIC data analysis")
column = st.sidebar.selectbox("Select column", ["Sex","Age","SibSp","Parch","Ticket","Fare","Cabin","Embarked"])
st.subheader(f"group by {column}")
df2 = df[["Survived", column]]
st.bar_chart(df2.groupby(column).count()["Survived"])
st.subheader(f"raw data")
st.write(df)