ためすう

第6章 部分から全体を組み立てる: Composite

2019-06-24

「Rubyによるデザインパターン」の読書メモです。

時々、個人の見解入りです。

「全体が部分のように振る舞う」という状況を表すデザインパターン

コンポジットオブジェクトの役割

1つにコンポジットオブジェクトはコンポーネントとしての役割を持っています。その一方で、コンポジットオブジェクトはコンポーネントのコレクションとしての役割も持っています。

class Task
  attr_reader :name

  def initialize(name)
    @name = name
  end

  def get_time_required
    0.0
  end
end

class CompositeTask < Task
  def initialize(name)
    super(name)
    @sub_tasks = []
  end

  def add_sub_task(task)
    @sub_tasks << task
  end

  def remove_sub_task(task)
    @sub_tasks.delete(task)
  end

  def get_time_required
    time = 0.0
    @sub_tasks.each {|task| time += task.get_time_required}
    time
  end
end

class AddDryIngredientsTask < Task
  def initialize
    super('Add dry ingredients')
  end

  def get_time_required
    1.0
  end
end

class MixTask < Task
  def initialize
    super('Mix that better up!')
  end

  def get_time_required
    3.0
  end
end

class MakeBatterTask < CompositeTask
  def initialize
    super('Make batter')
    @sub_tasks = []
    add_sub_task(AddDryIngredientsTask.new)
    add_sub_task(MixTask.new)
  end
end

p MakeBatterTask.new.get_time_required

演算子を実装したバージョン

class Task
  attr_reader :name

  def initialize(name)
    @name = name
  end

  def get_time_required
    0.0
  end
end

class CompositeTask < Task
  def initialize(name)
    super(name)
    @sub_tasks = []
  end

  def <<(task)
    @sub_tasks << task
  end

  def [](index)
    @sub_tasks[index]
  end

  def []=(index, value)
    @sub_tasks[index] = value
  end

  def remove_sub_task(task)
    @sub_tasks.delete(task)
  end

  def get_time_required
    time = 0.0
    @sub_tasks.each {|task| time += task.get_time_required}
    time
  end
end

class AddDryIngredientsTask < Task
  def initialize
    super('Add dry ingredients')
  end

  def get_time_required
    1.0
  end
end

class MixTask < Task
  def initialize
    super('Mix that better up!')
  end

  def get_time_required
    3.0
  end
end

class MakeBatterTask < CompositeTask
  def initialize
    super('Make batter')
    @sub_tasks = []
    @sub_tasks << AddDryIngredientsTask.new
    @sub_tasks << MixTask.new
  end
end

p MakeBatterTask.new.get_time_required

pandas の read_csv を使ってみる

2019-06-24

やったこと

pandas で csv ファイルを読み込みます。

確認環境

$ ipython --version
6.1.0
$ jupyter --version
4.3.0
$ python --version
Python 3.6.2 :: Anaconda custom (64-bit)
import pandas as pd
print(pd.__version__)
0.20.3

調査

read_csv

csv ファイルを読み込みます。

train = pd.read_csv("./train.csv")

戻り値は下記です。

DataFrame or TextParser

shape

shape を使って、行数と列数を確認します。

train.shape

(114321, 133)

size

train.size

15204693

参考

第5章 変更に追従する: Observer part2

2019-06-24

「Rubyによるデザインパターン」の読書メモです。

時々、個人の見解入りです。

注意点

Observerパターンを使うときに発生する問題の多くは、更新の頻度とタイミングについてです。

実際に何か変更されたのでなければ、おそらくオブザーバに通知すべきではありません。

変更された時のみ通知を送るようにするパターン

require 'observer'

class Payroll
  def update(changed_employee)
    puts("#{changed_employee.name}のために小切手を切ります!")
    puts("彼の給料はいま#{changed_employee.salary}です!")
  end
end

class Employee
  include Observable

  attr_reader :name, :title, :salary

  def initialize(name, title, salary)
    @name = name
    @title = title
    @salary = salary
  end

  # 変更があった時だけ、通知を送るようにします
  def salary=(new_salary)
    if @salary == new_salary
      nil
    end

    @salary = new_salary
    changed
    notify_observers(self)
  end
end

fred = Employee.new('Fred', 'Crane Operator', 3000)
payroll = Payroll.new
fred.add_observer(payroll)

fred.salary = 3000
fred.salary = 3100

変更をまとめて通知するパターン

require 'observer'

class Payroll
  def update(changed_employee)
    puts("#{changed_employee.name}のために小切手を切ります!")
    puts("title: #{changed_employee.title}")
    puts("彼の給料はいま#{changed_employee.salary}です!")
  end
end

class Employee
  include Observable

  attr_reader :name, :title, :salary

  def initialize(name, title, salary)
    @name = name
    @title = title
    @salary = salary
  end

  def salary=(new_salary)
    if @salary == new_salary
      nil
    end

    @salary = new_salary
  end

  def title=(new_title)
    if @title == new_title
      nil
    end
    @title = new_title
  end

  def changes_complete
    changed
    notify_observers(self)
  end
end

fred = Employee.new('Fred', 'Crane Operator', 3000)
payroll = Payroll.new
fred.add_observer(payroll)

fred.title = 'superman'
fred.salary = 9000
fred.changes_complete

Jupyter Notebook で全ての行を表示する

2019-06-23

やったこと

Jupyter Notebook で行数が多いデータを表示する時、省略されてしまうことがあります。

全部表示してみます。

ちなみに列数が多い場合は下記で対応できます。

確認環境

$ ipython --version
6.1.0
$ jupyter --version
4.3.0
$ python --version
Python 3.6.2 :: Anaconda custom (64-bit)
print(pd.__version__)
0.20.3

調査

csv ファイルを読み込みます。

train = pd.read_csv("./train.csv")
train.dtypes

出力は省略

最大の表示行数を 150 に設定します。

# 現在の設定確認
print(pd.get_option("display.max_rows"))
pd.set_option('display.max_rows', 150)

60

これで150行まで表示されるようになりました。

参考

Jupyter Notebook で全ての列を表示する

2019-06-23

やったこと

Jupyter Notebook で列数が多いデータを表示する時、省略されてしまうことがあります。

全部表示してみます。

確認環境

$ ipython --version
6.1.0
$ jupyter --version
4.3.0
$ python --version
Python 3.6.2 :: Anaconda custom (64-bit)
print(pd.__version__)
0.20.3

調査

csv ファイルを読み込みます。

train = pd.read_csv("./train.csv")
train.shape

(114321, 133)

最大の表示列数を 200 に設定します。

# 現在の設定確認
print(pd.get_option("display.max_columns"))
pd.set_option('display.max_columns', 200)

20

参考

pandas のデータ構造 (Series と DataFrame ) を学ぶ

2019-06-23

やったこと

pandas のデータ構造である

  • Series
  • DataFrame

について調べたことを書きます。

確認環境

$ ipython --version
6.1.0
$ jupyter --version
4.3.0
$ python --version
Python 3.6.2 :: Anaconda custom (64-bit)
import pandas as pd
print(pd.__version__)
0.20.3

調査

Series (シリーズ)

シリーズは1次元の配列のようなオブジェクトです。シリーズには、(NumPayが持つデータ型の)データ配列とそれに関連付けられたインデックスというデータラベルの配列が含まれます。

obj = Series([4, 7, -5, 3])
obj
0    4
1    7
2   -5
3    3
dtype: int64

DataFrame (データフレーム)

データフレームはテーブル形式のスプレッドシート風のデータ構造を持ち、順序付けられた列を持っています。各列には別々の型(数値型、文字列型、ブール型、など)を持たせることができます。データフレームは行と列の両方にインデックスを持っています。

data = {'state': ['Ohio', 'Nevada'],
       'year': [2000, 2001]}
df = DataFrame(data)
df

出力結果

state year
0 Ohio 2000
1 Nevada 2001

参考

  • Pythonによるデータ分析入門

Jupyter Notebook で実行時間を計測するために %%time を使う

2019-06-23

やったこと

Jupyter Notebook で処理の実行時間を計測する方法を調べました。

機械学習のモデルに学習させるときに、時間がかかりすぎることがあったため

実行時間を計測してみることにしました。

確認環境

$ ipython --version
6.1.0
$ jupyter --version
4.3.0
$ python --version
Python 3.6.2 :: Anaconda custom (64-bit)

調査

セルごとに実行時間を計測します。

%%time
import numpy as np
import pandas as pd
CPU times: user 7 µs, sys: 1 µs, total: 8 µs
Wall time: 11.2 µs

何度か繰り返し実行して、実行時間のレポートが出力されます。

%%timeit
import numpy as np
import pandas as pd
191 ns ± 2.12 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

行ごとの実行時間を計測します。

%time 2**128
%time 2**128
CPU times: user 3 µs, sys: 1 µs, total: 4 µs
Wall time: 16.2 µs
CPU times: user 2 µs, sys: 1 µs, total: 3 µs
Wall time: 5.96 µs
340282366920938463463374607431768211456

参考

Jupyter Notebook について

2019-06-23

やったこと

Jupyter Notebook について調べたことを書きます。

調査

Jupyter Notebook は、Jupyter プロジェクトによって開発されており、さまざまなプログラミング言語をサポートするインタラクティブなシェル環境である。

ここで、単語について記します。

IPython

インタラクティブシェル

Jupyter Notebook (IPython Notebook)

IPython をブラウザで利用できるようにしたもの

使ってみるにはどうするのかというと Anaconda をインストールすることで使えるようになります。

環境確認してみましょう。

$ ipython --version
6.1.0
$ jupyter --version
4.3.0
$ python --version
Python 3.6.2 :: Anaconda custom (64-bit)

公式ドキュメントはこちらです

参考

  • Python 機械学習プログラミング

How to Win a Data Science Competition (Week3-1 part2)

2019-06-23

Metrics optimization

学習目標

  • Describe the role of correct metric optimization method in a competition
  • Analyze new metrics
  • Create constant baselines
  • Recall the most important classification and regression metrics
  • Describe what libraries can be used to optimize a particular metric

Regression metrics review II

今回対象のメトリクス

  • ®MSPE, MAPE
  • ®MSLE

MSPE: Mean Square Percentage Error

$$ MSPE = \frac{100\%}{N}\sum_{i=1}^N(\frac{y_i - \hat{y_i}}{y_i})^2 $$

最適な定数は加重平均

MAPE: Mean Absolute Percentage Error

$$ MSPE = \frac{100\%}{N}\sum_{i=1}^N|\frac{y_i - \hat{y_i}}{y_i}| $$

最適な定数は加重中央値

®MSLE: Root Mean Square Logarithmic Error

対数スケールで計算された RMSE

$$ RMSLE = \sqrt{\frac{1}{N}\sum_{i=1}^N (log({y_i}+1) - log(\hat{y_i} + 1))^2} $$

$$ = RMSE(log({y_i}+1), log(\hat{y_i} + 1) = \sqrt{MSE(log({y_i}+1), log(\hat{y_i} + 1)} $$

対数空間で RMSE の定数を見つけ、対数空間から逆変換する必要がある

参考

第5章 変更に追従する: Observer part1

2019-06-22

「Rubyによるデザインパターン」の読書メモです。

時々、個人の見解入りです。

用語

Subject: サブジェクト

ニュースを持っているクラス

Observer: オブザーバ

ニュースを得ることに関心のあるオブジェクト

変更前

class Payroll
  def update(changed_employee)
    puts("#{changed_employee.name}のために小切手を切ります!")
    puts("彼の給料はいま#{changed_employee.salary}です!")
  end
end

class Employee
  attr_reader :name, :title, :salary

  def initialize(name, title, salary, payroll)
    @name = name
    @title = title
    @salary = salary
    @payroll = payroll
  end

  def salary=(new_salary)
    @salary = new_salary
    @payroll.update(self)
  end
end

payroll = Payroll.new
fred = Employee.new('Fred', 'Crane Operator', 3000, payroll)
fred.salary = 3500

継承を利用したパターン

class Subject
  def initialize
    @observers = []
  end

  def add_observer(observer)
    @observers << observer
  end

  def delete_observer(observer)
    @observers.delete(observer)
  end

  def notify_observers
    @observers.each do |observer|
      observer.update(self)
    end
  end
end

class Payroll
  def update(changed_employee)
    puts("#{changed_employee.name}のために小切手を切ります!")
    puts("彼の給料はいま#{changed_employee.salary}です!")
  end
end

class Employee < Subject
  attr_reader :name, :title, :salary

  def initialize(name, title, salary)
    super()
    @name = name
    @title = title
    @salary = salary
  end

  def salary=(new_salary)
    @salary = new_salary
    notify_observers
  end
end

fred = Employee.new('Fred', 'Crane Operator', 3000)
payroll = Payroll.new
fred.add_observer(payroll)

fred.salary = 3500

モジュールを利用したパターン

module Subject
  def initialize
    @observers = []
  end

  def add_observer(observer)
    @observers << observer
  end

  def delete_observer(observer)
    @observers.delete(observer)
  end

  def notify_observers
    @observers.each do |observer|
      observer.update(self)
    end
  end
end

class Payroll
  def update(changed_employee)
    puts("#{changed_employee.name}のために小切手を切ります!")
    puts("彼の給料はいま#{changed_employee.salary}です!")
  end
end

class Employee
  include Subject

  attr_reader :name, :title, :salary

  def initialize(name, title, salary)
    super()
    @name = name
    @title = title
    @salary = salary
  end

  def salary=(new_salary)
    @salary = new_salary
    notify_observers
  end
end

fred = Employee.new('Fred', 'Crane Operator', 3000)
payroll = Payroll.new
fred.add_observer(payroll)

fred.salary = 3500

ライブラリを利用したパターン

require 'observer'

class Payroll
  def update(changed_employee)
    puts("#{changed_employee.name}のために小切手を切ります!")
    puts("彼の給料はいま#{changed_employee.salary}です!")
  end
end

class Employee
  include Observable

  attr_reader :name, :title, :salary

  def initialize(name, title, salary)
    @name = name
    @title = title
    @salary = salary
  end

  def salary=(new_salary)
    @salary = new_salary
    changed
    notify_observers(self)
  end
end

fred = Employee.new('Fred', 'Crane Operator', 3000)
payroll = Payroll.new
fred.add_observer(payroll)

fred.salary = 3500