pandashでRedashのAPIが返す結果をPandasのDataFrameとして取得する

今日はRedash Advent Calendar 2017 17日目ですね。

qiita.com

mtomitomi.hatenablog.com

上記の記事ではRedashのAPIとPandasを組み合わせる例が紹介されていますが、私も似たようなことをやっているので、この記事では私が実務で使っている方法についても紹介してみます。

事前準備

Redash Advent Calendarでは定番になりつつある以下のリポジトリを使用して、Redashが利用できるようになっていることを前提にします。

github.com

クエリID1として、以下のクエリを登録しておきました。

SELECT * FROM city WHERE CountryCode = '{{CountryCode}}' ORDER BY Population DESC;

また、RedashユーザーのAPIキーが必要になりますが、その手順については、先程も紹介した記事に記載がありますので、そちらを参照してください。

RedashのAPI

業務でもヘビーにRedashを使っているのですが、Redashの /api/queries を例に取ると、アクセスはブラウザからでなくスクリプトからのアクセスが7割を超えています。

Redashのキラーフィーチャーは各社の導入目的によって異なるものだというのをAdvent Calendarに参加して強く感じていますが、私は以下の特徴についてRedashに大きな魅力を感じています。

  • 様々なデータソースに対応している
  • Webアプリとして提供されるためアプリをインストールする必要がなく、誰でもすぐに使い始められる
  • フォーク機能やクエリパラメータを使用して、エンジニアでなくてもある程度クエリをカスタマイズできる
  • APIが提供されているため、外部スクリプトでクエリ結果を使用した処理が作れる

あえて太字にしましたが、特にAPIが提供されているところが、私たちの生産性を大きく押し上げています。

私の場合、APIがなかったらここまでRedashにどっぷりハマることもなかったでしょう。

さらにRedashのAPI、特にクエリ実行をクエリパラメータつきで実行することができる、RedashDynamicQueryがPythonのモジュールとして使用できるので、Redashの便利な機能を気軽に使うことができます。

github.com

余談ですが、RedashDynamicQueryを見つけるまでは、APIではなくスクレイピングCSVダウンロードしていたこともあります。

RedashとPandas

さて、ここまではRedashとRedashDynamicQueryの紹介でしたが、Pythonでデータを扱うとなると、Pandasも関わりの強いモジュールといえます。

Python Data Analysis Library — pandas: Python Data Analysis Library

先程紹介したAdvent Calendarの記事でも、APIの結果をPandasのDataFrameにしてJupyter Notebookで扱うといった例になっていました。

Pandasはデータ操作の機能が便利なのはもちろんのこと、scikit-learnなどの機械学習ライブラリとの相性もいいので、どんなデータでもDataFrameに持ち込んで、あとはPandasで自由にデータ操作できるのがとても便利です。

私の場合、現時点ではRedashのクエリ結果をJOINすることが必要になった場合、RedashのPythonデータソースは使わずにAPIでクエリ結果を取得し、Pandas上で結合するようにしています。

余談ですが、PandasやDataFrameについて、個人的にはRebuildの以下のエピソードの中で出てきた、「DataFrame is JSON for Data Science」という表現がしっくりきました。

rebuild.fm

とにかくDataFrameにしてしまえばあとはどうにでもなる感じがしています。

pandash を使ってRedashのAPIの結果をPandasのDataFrameにする

では、実際にRedashのクエリ結果をPandasのDataFrameとして取得する例を紹介します。

Pandasだけでもそれほど難しい処理にはなりませんが、私は自作の pandash というモジュールを使っています。

github.com

前フリがかなり長くなりましたが、このモジュールを使ってRedashの結果を取得してみます。

インストール

環境に合わせて virtualenvvenv を使っていただくとして、モジュールはREADMEの通り以下のコマンドでインストールできます。

pandash は今のところPyPIには公開していません。

$ pip install redash-dynamic-query
$ pip install git+https://github.com/ariarijp/pandash

サンプルコード

以下のコードを main.py として保存します。

from redash_dynamic_query import RedashDynamicQuery

from pandash import query_to_df

redash = RedashDynamicQuery(endpoint='http://localhost',
                            apikey='REDASHのユーザーAPIキー',
                            data_source_id=1,
                            max_wait=60)

df = query_to_df(redash, 1, {'CountryCode': 'JPN'})

print(df.head(10)[['District', 'Name', 'Population']])

このコードでは、Redash上のクエリID1をクエリパラメータを指定して実行し、結果を上位10件の都道府県名、都市名、人口を表示しています。

これを実行してみると以下のような結果が表示されます。

$ python main.py
    District                 Name  Population
0   Tokyo-to                Tokyo     7980230
1   Kanagawa  Jokohama [Yokohama]     3339594
2      Osaka                Osaka     2595674
3      Aichi               Nagoya     2154376
4   Hokkaido              Sapporo     1790886
5      Kyoto                Kioto     1461974
6      Hyogo                 Kobe     1425139
7    Fukuoka              Fukuoka     1308379
8   Kanagawa             Kawasaki     1217359
9  Hiroshima            Hiroshima     1119117

とてもシンプルなコードでRedashのクエリ結果をPandasのDataFrameにすることができました。

まとめ

Redashのクエリ結果をPandasのDataFrameとして扱えるようになると、データをPandasの統一されたインターフェースで操作できるため、クエリ結果にひと手間かけたい、別の処理に渡す前の前処理をしたいなど、データ活用の幅が広がると思います。

よろしければRedash活用の際にRedashDynamicQueryやPandasとあわせて、 pandash も検討していただけるとうれしいです。

SchemaSpyを使ってRedashのテーブル定義確認を捗らせる

Redashのテーブル定義を確認しようとしたとき、 psql コマンドでも十分だけど、何か便利なツールを探してみたらSchemaSpyがよさそうだったのでメモ。

SchemaSpy • Database Documentation Built Easy.

ツール自体はJavaで書かれているようだけど、Dockerイメージが公開されているので以下の記事を参考にしてDocker上で動作させた。

qiita.com

使ってみる

Redashの検証環境を構築するときはDocker Composeを使っているので、Redashの docker-compose.production.yml を以下のように変更した。

diff --git a/docker-compose.production.yml b/docker-compose.production.yml
index f0b9812d..d4911ae0 100644
--- a/docker-compose.production.yml
+++ b/docker-compose.production.yml
@@ -50,3 +50,10 @@ services:
     links:
       - server:redash
     restart: always
+  schemaspy:
+    depends_on:
+      - postgres
+    image: schemaspy/schemaspy:snapshot
+    command: /entrypoint.sh -t pgsql -host postgres:5432 -db postgres -u postgres -hq
+    volumes:
+      - $PWD/schemaspy/output:/output

これでRedashを起動すると schemaspy/output ディレクトリーに結果が出力される。

index.html を開くと、テーブルの定義やER図が表示されて便利。

画面サンプル

テーブル一覧

f:id:ariarijp:20171216231344p:plain

テーブル定義

f:id:ariarijp:20171216231406p:plain

ER図

f:id:ariarijp:20171216231452p:plain

まとめ

DockerイメージになっているとJREをインストールしたりしなくて良いので楽。

もちろんRedash以外でも使えるツールなので、テーブル定義をさっと確認したいときは便利だと思う。

redashmanを使ってRedashのクエリをお手軽にバックアップする

今日はRedash Advent Calendar 16日目ですが、この記事はAdvent Calendarの記事ではありません。

qiita.com

12/16の記事はvankobeさんによる、Redashの結果をGoogleスプレッドシートで扱う事例の記事です。

qiita.com

Redashの便利さと、その裏にあるつらみ

Redashを活用しはじめて1年半ほど経ちますが、今ではRedashなしで業務が回らなくなるほどに使い倒しています。

しかし、Redashの活用が進んだことによって「Redashが落ちたら全てが止まる」といった状況になりつつあることも事実で、たまにRedashの収容サーバーからアラートが飛んでくると、背筋が凍る思いをしています。

それだけでなく、Redashの利点でもある「誰でもクエリを実行・編集できる」というのが、運用上問題になることも何度か経験してきました。

この記事ではRedashのクエリのバックアップについて、自作のツール redashman の宣伝もしつつ紹介していきます。

Redashのバックアップ

ざっくりですが、すぐ思いつくバックアップ方法は以下の2つと考えています。

  • Redashインスタンスのディスクイメージのスナップショットを取得する
  • Redashの管理DB(PostgreSQL)のDBバックアップを取得する

構成にもよりますが、ディスクイメージのバックアップはIaaSを使っていれば、もっとも簡単かつ安心できる方法かと思います。

管理DBのバックアップは、バックアップスクリプトや保存先を用意する必要がありますが、PostgreSQLについて少し知識があれば簡単に実現できる方法と言えます。

上記のように、ディスクやデータベースの単位でバックアップをとっておくことは運用上重要なことですが、「クエリ」のバックアップについても考えてみます。

Redashは誰でも気軽にクエリを実行・編集することができる一方で、悪意の有無に関わらず、誤ってクエリを編集してしまい、クエリが実行できなくなってしまうことが考えられます。

しかし、先に紹介したディスクやデータベースによるバックアップは「クエリ」のバックアップという点で考えるとリストアの手間もあり、少し大げさに感じる部分があります。

redashman について

現在、私は redashman という自作のツールを用いて、Redashのクエリを定期的にバックアップしています。

github.com

Redashを管理するので redashman という名前にしましたが、なぜか青いロボットが針に当たってティウンティウンする光景が目に浮かんだかたは個別にご連絡ください。私はX4が好きです。

このツールはクエリの一覧、取得、作成などをCLIから実行するためのツールとして作成しましたが、その中にクエリの定義を取得するものがあるので、それを使ってクエリをバックアップしています。

インストール

redashman はGoで書いたので、go get で簡単にインストールできます。

$ go get -u github.com/ariarijp/redashman

redashman を使用したクエリのお手軽バックアップ

redashman 自体の使い方についてはREADMEを参照していただくとして、付属のPythonスクリプトを使用してバックアップをしてみます。

$ mkdir redash_backup
$ cd redash_backup
$ cp $GOPATH/src/github.com/ariarijp/redashman/scripts/backup_queries.py ./
$ export REDASH_URL=http://localhost
$ export REDASH_API_KEY="Redashのadmin権限があるユーザーのAPIキー"
$ python ./backup_queries.py
$ ls -la backup
total 48
drwxr-xr-x  8 ariarijp  wheel  272 12 16 14:34 .
drwxr-xr-x  4 ariarijp  wheel  136 12 16 14:34 ..
-rw-r--r--  1 ariarijp  wheel   23 12 16 14:34 0001.sql
-rw-r--r--  1 ariarijp  wheel   51 12 16 14:34 0002.sql
-rw-r--r--  1 ariarijp  wheel   89 12 16 14:34 0003.sql
-rw-r--r--  1 ariarijp  wheel   83 12 16 14:34 0004.sql
-rw-r--r--  1 ariarijp  wheel   89 12 16 14:34 0005.sql
$ cat backup/0001.sql
SELECT * FROM country;

これでクエリのバックアップを取得することができるようになりました。

定期的にバックアップし、変更履歴も残す

定期的にバックアップをするには、もう一手間かける必要があります。

そのために、以下のスクリプトbackup.sh として保存し、実行権限を与えておきます。

#!/bin/bash

export REDASH_URL=http://localhost
export REDASH_API_KEY="Redashのadmin権限があるユーザーのAPIキー"

cd `dirname $0`

if [ ! -d '.git' ]; then
  git init
fi

python backup_queries.py
git add . && git ci -a -m `date '+%Y%m%d%H%M%S'`

これを実行すると、先ほどのPythonスクリプトでクエリのバックアップを取得し、そのファイルがGitリポジトリにコミットされます。

$ ./backup.sh
Initialized empty Git repository in /private/tmp/redash_backup/.git/
[master (root-commit) 820ae32] 20171216144840
 8 files changed, 49 insertions(+)
 create mode 100755 backup.sh
 create mode 100644 backup/0001.sql
 create mode 100644 backup/0002.sql
 create mode 100644 backup/0003.sql
 create mode 100644 backup/0004.sql
 create mode 100644 backup/0005.sql
 create mode 100644 backup_queries.py

このスクリプトcron などを使って適当な間隔で定期実行すれば、クエリのバックアップを定期的に、かつ、Gitを使用することで履歴とタイムスタンプ付きで保存することができるようになります。

まとめ

Redashの気軽さを維持しつつ、redashman でクエリを定期的にバックアップすることで、クエリを誤って編集してしまったなどのトラブルに対処できるようになり、副産物としてクエリの編集履歴もGitという多くの開発者の手に馴染んだツールで管理できるようになります。

Redashの利用をこれから検討している方にも、ぜひ redashman の導入を検討してみてほしいです。

宣伝

id:kakku22 と私の主催で、ハンズオン形式のRedash Meetup #0 を12/19(火)に開催しますが。ありがたいことにキャンセル待ちが出ています。

redash-meetup.connpass.com

ハンズオンイベントについては来年1月に再演を計画していますので、今回予定が合わなかったり、キャンセル待ちから繰り上がらなかった方も、改めて参加を検討していただけるとうれしいです。

また、今後はハンズオンイベントではなく、各社の導入事例を成功も失敗も含めて共有できるような企画も検討しておりますので、Redashに興味があるかたは、よろしければConnpassのRedash Meetupグループに参加してみてください。

redash-meetup.connpass.com

このグループに参加していると、次回のハンズオンも含め、今後のイベント日程もチェックしやすくなると思います。

Redashのクエリエディターのテーマを変える

この記事はRedash Advent Calendar 2017の記事ではありません。

qiita.com

9日目の記事は take4_k さんの「RedashのAzure Table StorageのQuery Runnerを作った 」です。この記事を読む前にぜひ読んでください。

take4.hatenablog.com

やりたいこと

f:id:ariarijp:20171209001542p:plain

この記事では、こんな感じにクエリエディターのテーマを好みのものに変えてみます。

環境構築

おなじみの以下のドキュメントにそって開発環境を構築してある前提です。

Docker Based Developer Installation Guide · Redash Help Center

JSを書き換える

Redashのクエリエディターは Brace というNPMモジュールで作られています。

github.com

Braceの中で使用していると思われる Ace Editor がテーマ切り替えをサポートしているので、その機能を使ってテーマを切り替えます。

Ace - The High Performance Code Editor for the Web

テーマをデフォルトのものから Tommorow Night Bright に変えるため、 client/app/components/queries/query-editor.js を書き換えます。

diff --git a/client/app/components/queries/query-editor.js b/client/app/components/queries/query-editor.js
index 445be461..29bf5494 100644
--- a/client/app/components/queries/query-editor.js
+++ b/client/app/components/queries/query-editor.js
@@ -3,6 +3,8 @@ import 'brace/mode/python';
 import 'brace/mode/sql';
 import 'brace/mode/json';
 import 'brace/ext/language_tools';
+import 'brace/theme/tomorrow_night_bright';
+
 import { map } from 'underscore';

 // By default Ace will try to load snippet files for the different modes and fail.
@@ -46,6 +48,8 @@ function queryEditor(QuerySnippet) {
             editor.commands.bindKey('Cmd+L', null);
             editor.commands.bindKey('Ctrl+L', null);

+            editor.setTheme('ace/theme/tomorrow_night_bright');
+
             QuerySnippet.query((snippets) => {
               window.ace.acequire(['ace/snippets'], (snippetsModule) => {
                 const snippetManager = snippetsModule.snippetManager;

変更できたらビルドします。

$ npm run build

動作確認

docker-compose でRedashを起動し、管理ユーザーやデータソースを作成した後にクエリ作成画面を表示すると、この記事の冒頭の画像のようにテーマが Tomorrow Night Brightになっています。

他のテーマを使用したい場合は以下を参考にしてみてください。

github.com

まとめ

実用上はもう少し工夫して実装した方が良さそうですが、今後Redashの設定ファイルでテーマが切り替えられたりすると面白いかもしれませんね。

Redashで独自のQuery Runnerを作る

この記事はRedash Advent Calendar 2017 8日目の記事です。

qiita.com

独自のQuery Runnerを作る

いきなり元ネタを出しますが、Redashの開発者Arikさんによる、以下の投稿を見るとだいたい作り方の雰囲気がわかってきます。

discuss.redash.io

上記の記事を参考に、クエリの代わりに文字列を貼り付けると、その文字列をCSVのようにパースして結果を返すような、ちょっと変わったQuery Runnerを作成します。

開発環境

細かい開発環境構築方法の説明は割愛しますので、ご了承ください。

以下の公式ドキュメントに開発環境の構築手順が記載されているため、この手順に沿って、Docker上の開発環境が整っていることを前提に進めます。

Docker Based Developer Installation Guide · Redash Help Center

なお、使用しているRedashのバージョンは master ブランチの以下のコミット時点のもので、v4.x系に該当するものだと思います。

github.com

サンプルコード

以下のスクリプトredash/query_runner/csv_parser.py として保存します。

import csv as csv
import json

from redash.query_runner import BaseQueryRunner, register


class CsvParser(BaseQueryRunner):
    @classmethod
    def configuration_schema(cls):
        return {
            'type': 'object',
            'properties': {
                'delimiter': {
                    'type': 'string',
                    'title': 'Delimiter'
                }
            }
        }

    @classmethod
    def annotate_query(cls):
        return False

    def __init__(self, configuration):
        super(CsvParser, self).__init__(configuration)

    def test_connection(self):
        pass

    def run_query(self, query, user):
        data = {
            'columns': [],
            'rows': [],
        }

        delimiter = str(self.configuration.get('delimiter'))

        for row in csv.DictReader(query.strip().splitlines(), delimiter=delimiter):
            if len(data['columns']) == 0:
                for key in row.keys():
                    data['columns'].append({'name': key, 'friendly_name': key})

            data['rows'].append(row)

        return json.dumps(data), None


register(CsvParser)

コードの詳細は割愛しますが、クエリ文字列をCSVとしてパースし、Redashのクエリ結果の形式に準拠した形に整形します。

CSVの1行目はヘッダーとして扱うようにしています。

区切り文字はデータソースの設定として定義できるようになっており、デフォルトは,(半角カンマ)としています。

本来はカラムには string などの型を明示することも可能ですが、このスクリプトでは省略しています。

docker-compose.ymlの変更

独自のQuery Runnerを使用するため、 docker-compose.yml を編集します。

以下は docker-compose.yml の差分になります。

diff --git a/docker-compose.yml b/docker-compose.yml
index 536dd446..2f948d90 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -17,6 +17,7 @@ services:
       REDASH_LOG_LEVEL: "INFO"
       REDASH_REDIS_URL: "redis://redis:6379/0"
       REDASH_DATABASE_URL: "postgresql://postgres@postgres/postgres"
+      REDASH_ADDITIONAL_QUERY_RUNNERS: "redash.query_runner.csv_parser"
   worker:
     build: .
     command: scheduler
@@ -31,6 +32,7 @@ services:
       REDASH_DATABASE_URL: "postgresql://postgres@postgres/postgres"
       QUEUES: "queries,scheduled_queries,celery"
       WORKERS_COUNT: 2
+      REDASH_ADDITIONAL_QUERY_RUNNERS: "redash.query_runner.csv_parser"
   redis:
     image: redis:3.0-alpine
     restart: always

環境変数 REDASH_ADDITIONAL_QUERY_RUNNERS で追加のQuery Runnerを指定できます。環境変数についてはAdvent Calendar 2日目の記事がとても参考になるのでおすすめです。

qiita.com

ここまでで独自Query Runnerを使うための準備は完了です。

動作確認

早速、 docker-compose up して動作確認します。

データベースの作成や管理ユーザの作成については済んでいるものとして進めます。

データソースの作成

データソースの作成画面でTypeのプルダウンをクリックすると、以下のように CsvParser が追加されています。

f:id:ariarijp:20171207195907p:plain

CsvParser を選択すると、以下のようなフォームが表示されます。

f:id:ariarijp:20171207200120p:plain

データソース名は CSV として、区切り文字はデフォルトで , となっているので、あえて :(半角コロン)にしてデータソースを保存します。

クエリの実行

クエリの作成画面に移動し、データソースとして CSV を選択ます。

クエリには以下のような半角コロン区切りの文字列を入力します。

name:ring_name:finishing_move:born_on
Kanji Inoki:Antonio Inoki:Enzuigiri:1943-02-20
Baba Shohei:Giant Baba:Big boot:1938-01-23

テストデータの内容については特に触れず、実行してみます。

f:id:ariarijp:20171207200851p:plain

ここまでの手順に問題がなければ、上のように文字列をパースしてクエリ結果として表示することができます。

カラムの並び順が入力したものと違っているのは、PythonのDictが挿入順を保持しないからでしょうか。Redashは現時点でPython2.7を使用していますが、Python3.6あたりで順序が保持されるようになるので、Python3対応されたら少し結果が変わると思います。

1日目の記事で私が紹介した Query Results データソースで別の結果と結合することもできたりして面白いかもしれませんね。

ariarijp.hatenablog.com

まとめ

この記事ではあまり実用性を考えずに独自のQuery Runnerを作成しましたが、Redashをチームや事業にフィットするようにカスタマイズしたいと考えられる場合、Query Runnerを作るというのは選択肢にいれてみてはいかがでしょうか。

明日は take4_k さんの「azure table storageのquery runner作ってみたので書きます」です。この記事がいい前振りになることを願っています。