歩いたら休め

If the implementation is easy to explain, it may be a good idea.

【Python】RプログラマーのためのPython入門

会社に優秀な後輩が入ってきて、優秀な先輩(私でゎない)の助けを得ながら、立派な分析者・Rプログラマーとして成長しつつあります。

しかし、R言語だけで全ての作業が完結できるわけではありません。手元でデータを加工・分析するための環境としては素晴らしいのですが、大規模な計算では遅かったり(パフォーマンスを上げるにしても工夫が必要だったり)、クラスベースのオブジェクト指向が無いため、プログラムが大きくなるにつれて関数の整理が難しかったり、言語としてつらい面も多いです。

また、データ分析して作ったモデルをサービスに乗せる際には別の言語を使う必要があると思います。一応、shinyというWEBアプリを作るためのライブラリもあるものの、「社外向けのサービスでバリバリ使ってるぜ!」という話は聞いたことがありません。

というわけで、R言語メインのプログラマーが、一歩進んでスクリプト言語Python)が抵抗なく使い始められるようにサポートするための記事を書きました。

こちらの記事の逆バージョンですね。

kiito.hatenablog.com

ただし、自分の環境であるPython 3.4.3 :: Anaconda 2.0.1 (x86_64)で確認していますが、バージョン(特に2.*系)によって微妙に異なる部分があるかもしれませんのでご了承ください。

Pythonの情報源・入門書

Pythonの書き方自体は難しくありません。入門書を読めば優秀なRプログラマーであればなんとなく分かると思います。 適当な入門書を読んだ後は、Dive Into Python 3 日本語版を読むと理解が深まるでしょう。

ただし、それだけで分析・開発できるようになるわけではありません。 数値計算や可視化のライブラリや、ツールとしての使い方を勉強する必要があります。

今となってはPythonのバージョン等がちょっと古いですが、それでも網羅的に紹介されています。

Pythonによるデータ分析入門 ―NumPy、pandasを使ったデータ処理

Pythonによるデータ分析入門 ―NumPy、pandasを使ったデータ処理

上記の本で古くなった部分は、『IPythonデータサイエンスクックブック』でアップデートできると思います。 が、私もちゃんとこの本は読んでいないのできちんと紹介できません。

ただ、Pythonの高速化の例の中で、CやCythonに混じってJuliaが紹介されていたのは驚きました。

少し毛色は違いますが、『Pythonプロフェッショナルプログラミング』もいい本です。 PythonでのWEB開発のもろもろを書いた本で、課題管理やレビュー等、Pythonを使って開発している人が悩むことがとりあえず全部書かれているような本です。

Pythonプロフェッショナルプログラミング第2版

Pythonプロフェッショナルプログラミング第2版

Pythonの分析環境構築

Anacondaをインストールしましょう。Anacondaとはpythonディストリビューションの一つで、nupyやscipy、matplotlib等の数値計算やデータ分析で必要な主要ライブラリをオールインワンでインストールできます。

これらの記事を参考にインストールしてください。

qiita.com

antibayesian.hateblo.jp

Anacondaはデータマイニングの便利なライブラリを集めて一括でインストール・利用できるようにしたパッケージです。内包されているライブラリは様々な数値解析、機械学習自然言語処理、可視化、DB連携、データハンドリング、さらには最近話題のディープラーニングなど多岐に渡り、これを入れておけばデータマイニングを行う大抵の場面で対応できるでしょう。

RプログラマーはRStudioを利用している方がほとんどでしょうが、Pythonでデータ分析する際に便利なのがjupyter notebookです。 かつてipython notebookと呼ばれていたツールが、他言語でも利用できる形になったものです。

詳しい話は以下の記事を読んでください。

myenigma.hatenablog.com

for文でイライラしない方法

Rのプログラマーはほとんどfor文は使わないと思います。 もし使うとしても、「シミュレーションで1000ステップ計算する」というような場面に限られており、データの操作にfor文は不要です。

数値計算であればベクトル演算を使い、リストの操作もlapply等の汎関数高階関数)を利用していることと思います。 例えば、1から10までの数を2倍したベクトルを得るには、Rであれば簡単に書けます。

1:10 * 2
#  [1]  2  4  6  8 10 12 14 16 18 20

ところが、他の大抵の言語にはベクトル型は無く、リストや配列が用意されています。 そのため、for文を使って次のようなコードを書くことが多く、「Rであればもっと簡潔に書けるのに」とイライラしていることと思います。

result = []
for i in range(1, 11): # 第二引数を10にすると、1:9までしか得られないので注意!
    result.append(i * 2)
print(result)
# [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]

# 第三引数で増分が指定できるから、
# list(range(2, 22, 2))でも同じ値は得られるんだけどね

このような場合、Pythonであればリスト内包表記を使うか、numpyという数値計算のライブラリを使ってベクトル型を導入しましょう。

まず、numpyを使った例です。

import numpy as np
np.arange(1, 11) * 2
# array([ 2,  4,  6,  8, 10, 12, 14, 16, 18, 20])

次にリスト内包表記です。これは関数型言語(たしかHaskell)を参考にして導入された書き方です。

Pythonではfor等の手続き型言語の書き方を流用しているのでわかりづらいですが、{f(x) | x ∈ A}(Aという集合の中のxという変数にfという計算を適用する)という数式を[f(x) for x in A]と表現しています。ちなみにHaskellの書き方のほうが、元々の数式に近くて分かりやすいです

[i*2 for i in range(1, 11)]
# [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]

「何度もリストに対して計算を行う場合、リスト内包表記を使って何度もループさせると遅くなるんじゃないの?」と疑問に思うかもしれませんが、Pythonイテレータを遅延評価させる機能があります。 とりあえず覚えなくても問題ないので詳しくは説明しませんが、以下の記事や『Effective Python』を読んでください。

Python のジェネレータ (1) - 動作を試す | すぐに忘れる脳みそのためのメモ

Effective Python ―Pythonプログラムを改良する59項目

Effective Python ―Pythonプログラムを改良する59項目

はじめてのクラス

R言語プログラマーが他言語に行って戸惑うのが、クラスベースのオブジェクト指向だと思います。

たとえば、Rでの分析しか経験してこなかったプログラマーが、 「ルーチンの分析処理をバッチ処理で自動化したい!」と思って開発にも携わるようになると、 クラスという見慣れない概念が出てきて戸惑うことになると思います。

# クラスを作る
class MyClass:
    # クラスのインスタンスを作る際に呼び出させる関数(initializeの略)
    def __init__(self, config):
        # インスタンス変数に設定(conf)を保存する
        self._config = config

    # 設定をprintする
    def print_conf(self):
        print(self._config)

if __name__ == '__main__':
    instance = MyClass('variable')
    instance.print_conf()

Rのプログラマーはクラスという概念に慣れていないため、「そんなものなくても関数を細かく区切っていけばそれほど困らないよ」「継承とかカプセル化とか難しい用語がいっぱいでわからないよ(もっと難しい統計用語は使いこなしているクセして!)」と思いがちですが、実際使えると便利な機能です。

(これだけが全てだとは思いませんが、)クラスとは概ね「デカいプログラムを整理するためのもの」です。 たしかに関数を細かく区切ることで分かりやすい、似たような機能をたくさん実装する場合など、オブジェクト指向の機能を使うとコードを整理することができます。

一例として、私は最近作った、社内の様々なレポートを取得するRのコードを残しておきます。クラスの機能を導入するために、R6ライブラリを使いました

実際には「売上レポートを取得するための機能」の他に「コンバージョン率のレポートを取得する機能」などをたくさん作っており、そちらではreplaceQueryYearの代わりにreplaceQueryDateが必要だったりと、「この関数ってどこに使ってたんだっけ?」「似たような名前の関数がたくさんできちゃったよ」とごちゃごちゃしそうだったのでクラスを作って整理しました。

なんとなくクラスを覚える利点(の一つ)を感じてもらえればと思います。

# PythonではなくRだよ!
# 「SQLのファイルを読み込む」など、いろいろなクラスで共通で使う関数をまとめたクラス
AbstructReporter <- R6::R6Class("AbstructReporter",
  private = list(
    db_config = NA,

    scanQuery = function(query_path) { 
      return (
        # query_pathにあるSQLファイルからクエリを読み込む
      )
    },

    execQuery = function(sql) {
      return (
        # sqlを実行する
      )
    }
  )
)

# 売上レポートを取得するためのクラス
SalesReporter <- R6::R6Class("SalesReporter",
  inherit = AbstructReporter, # 共通で使うクラスを継承

  initialize = function(db_config) {
    private$db_config = db_config # DBの設定を
  },

  public = list(
    getSalesReport = function(target_year) {
      sql <- private$scanQuery("example/file/path.sql") %>%
        private$replaceQueryYear(target_year)
      return (
        private$execQuery(sql)
      )
    }
  ),

  private = list(
    replaceQueryYear = function(sql, target_year) {
      return (
        # SQLの対象期間(年)を置き換える
      )
    }
  )
)

# 実行プログラム
db_config <- "DBの設定"
instance <- SalesReporter$new(db_config)
sales_report <- instance$getSalesReport("2016") # 2016年のレポートを取得する

RでもR3とかR4とかいくつかのオブジェクト指向的な機能が実装されているようなのですが、『R言語徹底解説』を読んでもサッパリ分かりません。誰か助けてください。

また、「オブジェクト指向」という概念は、以下の記事を読めば分かる通り、いろいろなプログラミングの概念がごった煮になった一筋縄ではいかないものです。 いろいろな言語に触ってがんばっていきましょう(私もがんばります)。

qiita.com

Pythonは関数の第一引数にselfが必要だったり変な感じがするので、オブジェクト指向周りの概念はRubyで勉強するのが近道かなと思います(Python入門なのに)。Rubyは最初からオブジェクト指向言語を目指して作られているので、スッキリ学びやすかったです。 『メタプログラミングRuby 第2版』の前半で、Rubyオブジェクト指向の仕組みが説明されています。

Pythonな人だけど「メタプログラミングRuby」を読んでみた | TRIVIAL TECHNOLOGIES 4 @ats のイクメン日記

メタプログラミングRuby 第2版

メタプログラミングRuby 第2版

もしかするとJavaのほうがいいのかもしれませんが、私はJavaをほとんど触ったことがないので分かりません。

ファイルを読み込む

Rでファイルを読み込む場合、分析用ファイルであればread.csvや(私はよく知らないですが)データを読み込む高速なライブラリの関数を、テキストファイルであればscanreadLines等の関数を利用していると思います。

ファイルを読み込む際にプログラマーが気をつけることは、「(特にエラーが発生したときに)ファイルを閉じ忘れない」ということです。 そのため、大抵の言語ではファイルの閉じ忘れを防ぐ機能を持っています。

Pythonでは、with構文でその機能を提供しています。withブロックを抜ける(エラーが発生した場合でも)と、自動でファイルを閉じてくれます。 プログラミングでは、ファイルのインプット/アウトプットで特にエラーが発生しやすいため、このような処理が用意されています。 他の言語でも同様の機能が用意されているものが多く、Rubyの場合ブロックを使ってこの機能を実現しています

with open('./example.txt', 'r') as f:
    text = f.read()
print(text) # テキストファイルの中身を表示!

open関数の第二引数の'r'はread=読み込みの意味です。ファイルに書き込む場合は'w'にします。

csvを読み込む際は、標準ライブラリであるcsvを利用します。公式のドキュメントにあるサンプルコードを引っ張ってきました。

import csv
with open('some.csv', newline='') as f:
    reader = csv.reader(f)
    for row in reader:
        print(row)

with構文についていまいちピンと来ていない場合、ちょっと古いですが、こちらの記事が参考になると思います。

reiki4040.hatenablog.com

「おれはread.csvしたいだけなのに面倒だなあ」「Rみたいにデータフレーム使いたいよ」という場合は、pandasライブラリを利用しましょう。 もちろんread.csvにあたる操作も用意されています。

import pandas as pd
sampledata = pd.read_csv('./example.csv')

また、write.csvに当たる操作は、データフレーム型の変数からto_csvメソッドを呼び出す形で操作します。

sampledata.to_csv('output.csv', index = False)

pandasの使い方の詳しくは、以下のサイトを参照してください。

myenigma.hatenablog.com

既存のライブラリを使う

R言語プログラマーは、自分で計算のロジックを実装することは(周囲から思われているよりは)あまり無く、CやC++で作成された高速なライブラリを組み合わせて分析を行うことが多いと思います。

Pythonであれば、科学計算のscipy機械学習scikit-learn等、Rほどではないですが充実したライブラリを利用することができます。 これらのライブラリについて調べれば、利用したい処理が既に実装されていることも多いでしょう。

例えば、以下の記事ではscipyによる高速フーリエ変換(FFT)の例が紹介されています。

org-technology.com

記事を見てもらえばわかると思いますが、フーリエ変換のような自分で実装するとややこしい処理が、fft関数を呼び出すだけで完了してしまっていることがわかると思います。

# 離散フーリエ変換
yf = fft(y)

プロットする

自分の持っている仮説を検証するには、集計・解析結果をグラフにプロットしてPDCAを回す必要があります。

Rでは標準でplothist等の関数を使ってグラフを可視化できますが、Pythonでは同等の機能をmatplotlibを利用します。これはMATLAB風のプロットが簡単にできるライブラリです。

Jupyter notebook上でインタラクティブに可視化でき、Rのプログラマーでも特に違和感なく利用できると思います。

hirotsuru.hatenablog.com

ただし、最近はseabornbokeh等の新しい可視化ライブラリが現れてきているようです。特にseabornは、Rのggplot2に近いおしゃれな感じのプロットが簡単にできるそうなので、今度触ってみます。

この辺の話は『IPythonデータサイエンスクックブック』にきちんと書いてあるそうです。

myenigma.hatenablog.com

qiita.com