ためすう

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

Ruby で MessagePack を使ってみる

2019-06-21

やったこと

MessagePack を使ってみます。

確認環境

$ ruby --version
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin17]

$ gem list | grep msg
msgpack (1.2.10)

調査

gem インストール(もし必要だったら)

Gemfile

gem 'msgpack'
$ bundle install

pack と unpack してみる

MessagePack is an efficient binary serialization format

圧縮効率が良いらしいです。

$ rails c
Running via Spring preloader in process 56887
Loading development environment (Rails 5.2.3)
irb(main):001:0> msg = [1, 2, 3, "漢字"].to_msgpack
=> "\x94\x01\x02\x03\xA6\xE6\xBC\xA2\xE5\xAD\x97"
irb(main):002:0> MessagePack.unpack(msg)
=> [1, 2, 3, "漢字"]

メモリのサイズを見てみる

$ rails c
Running via Spring preloader in process 57354
Loading development environment (Rails 5.2.3)
irb(main):001:0> v = [1, 2, 3, "漢字"]
=> [1, 2, 3, "漢字"]
irb(main):002:0> require 'objspace'
=> true
irb(main):003:0> p ObjectSpace.memsize_of(v)
72
=> 72
irb(main):004:0> msg = [1, 2, 3, "漢字"].to_msgpack
=> "\x94\x01\x02\x03\xA6\xE6\xBC\xA2\xE5\xAD\x97"
irb(main):005:0> p ObjectSpace.memsize_of(msg)
40
=> 40

オブジェクトは圧縮されているようです。 (memsize_of で返される値は完全ではないらしい)

参考

第4章 アルゴリズムを交換する: Strategy part2

2019-06-20

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

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

クラスベースの Strategy ではなく、コードブロックベースの Strategy パターンで書いてみます。

Strategy を適用する (簡易版)

class Report

  attr_reader :title, :text
  attr_accessor :formatter

  def initialize(&formatter)
    @title = '月次報告'
    @text = ['順調', '最高']
    @formatter = formatter
  end

  def output_report
    @formatter.call(self)
  end
end

HTML_FORMATTER = lambda do |context|
  puts '<html>'
  puts "<head><title>#{context.title}</title></head>"
  puts('<body>')
  context.text.each do |line|
    puts("<p>#{line}</p>")
  end
  puts('</body>')
  puts '</html>'
end

PLAIN_TEXT_FORMATTER = lambda do |context|
  puts("*****#{context.title}*****")
  context.text.each do |line|
    puts(line)
  end
end

report = Report.new &HTML_FORMATTER
report.output_report

report = Report.new &PLAIN_TEXT_FORMATTER
report.output_report

コードブロックのストラテジは、そのインターフェースが単純で、1つのメソッドで事足りるようなときにのみ有効に働きます。

sidekiq で ログ出力する

2019-06-20

やったこと

Rails5 で利用している sidekiq のログを出力します。

確認環境

$ ruby --version
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin17]

$ rails --version
Rails 5.2.3

$ gem list | grep sidekiq
sidekiq (5.2.7)

前提

sidekiq がインストール済みとします。

Rails5 で sidekiq を使う – ためすう

調査

ログ出力の設定

config/initializers/sidekiq.rb

Sidekiq::Logging.logger.level = Logger::DEBUG

sidekiq を再起動

$ bundle exec sidekiq -C config/sidekiq.yml

ログを確認

queue を登録して、worker を実行すると下記のログが流れます。

$ less log/sidekiq.log

2019-06-15T14:17:59.230Z 61557 TID-ovrdi8s0t TaskWorker JID-fe2e63bbd5eb7d0221a72f55 INFO: start
2019-06-15T14:17:59.233Z 61557 TID-ovrdi8s0t TaskWorker JID-fe2e63bbd5eb7d0221a72f55 INFO: id 出力
2019-06-15T14:17:59.233Z 61557 TID-ovrdi8s0t TaskWorker JID-fe2e63bbd5eb7d0221a72f55 INFO: 333
2019-06-15T14:17:59.233Z 61557 TID-ovrdi8s0t TaskWorker JID-fe2e63bbd5eb7d0221a72f55 INFO: done: 0.003 sec

参考

第4章 アルゴリズムを交換する: Strategy part1

2019-06-19

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

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

継承に基づいた設計は、いくつかの深刻な不利益をもたらします。たとえどんなに注意深くコードを設計しても、サブクラスはそのスーパークラスに依存してしまいます。

そこで、「委譲」を使ってコードを書き直してみます。

Strategy を適用する (version1)

class Formatter
  def output_report(title, text)
    raise 'Abstract method called'
  end
end

class HTMLFormatter < Formatter
  def output_report(title, text)
    puts '<html>'
    puts "<head><title>#{@title}</title></head>"
    puts('<body>')
    text.each do |line|
      puts("<p>#{line}</p>")
    end
    puts('</body>')
    puts '</html>'
  end
end

class PlainTextFormatter < Formatter

  def output_report(title, text)
    puts("*****#{title}*****")
    text.each do |line|
      puts(line)
    end
  end
end

class Report

  attr_reader :title, :text
  attr_accessor :formatter

  def initialize(formatter)
    @title = '月次報告'
    @text = ['順調', '最高']
    @formatter = formatter
  end

  def output_report
    @formatter.output_report(@title, @text)
  end
end

report = Report.new(HTMLFormatter.new)
report.output_report

report.formatter = PlainTextFormatter.new
report.output_report

formatter を外から注入しているので、依存性の注入と言えそうです。

コンテキストとストラテジの間でのデータを共有する方法

  • コンテキストがストラテジオブジェクトのメソッドを呼び出すときに引数で渡す
  • ストラテジへと渡されたコンテキスト自身の参照を使う
    • データの流れが単純化されますが、コンテキストとストラテジ間の結合度を上げてしまうことにもなります。

Strategy を適用する (version2)

Formatter クラスはインターフェースを規定するためにしか、存在しません。

Ruby のダックタイピングの哲学に反するため、削除します。

class HTMLFormatter
  def output_report(title, text)
    puts '<html>'
    puts "<head><title>#{@title}</title></head>"
    puts('<body>')
    text.each do |line|
      puts("<p>#{line}</p>")
    end
    puts('</body>')
    puts '</html>'
  end
end

class PlainTextFormatter

  def output_report(title, text)
    puts("*****#{title}*****")
    text.each do |line|
      puts(line)
    end
  end
end

class Report

  attr_reader :title, :text
  attr_accessor :formatter

  def initialize(formatter)
    @title = '月次報告'
    @text = ['順調', '最高']
    @formatter = formatter
  end

  def output_report
    @formatter.output_report(@title, @text)
  end
end

report = Report.new(HTMLFormatter.new)
report.output_report

report.formatter = PlainTextFormatter.new
report.output_report

sidekiq で namespace を使えるようにする

2019-06-19

やったこと

Rails5 で sidekiq を利用しているのですが、queue に namespace を指定するとエラーが発生しました。

今回はこのエラーを解消を目指します。

確認環境

$ ruby --version
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin17]

$ rails --version
Rails 5.2.3

$ gem list | grep sidekiq
sidekiq (5.2.7)

前提

sidekiq がインストール済みとします。

Rails5 で sidekiq を使う – ためすう

調査

発生したエラー

config/initializers/sidekiq.rb

Sidekiq.configure_server do |config|
  config.redis = { url: 'redis://localhost:6379', namespace: 'sidekiq' }
end

namespace を指定しました。

エラー

2019-06-15T07:14:53.315Z 47524 TID-ov7xxfh88 INFO: Booting Sidekiq 5.2.7 with redis options {:url=>"redis://localhost:6379", :namespace=>"sidekiq", :id=>"Sidekiq-server-PID-47524"}
2019-06-15T07:14:53.426Z 47524 TID-ov7xxfh88 INFO: Running in ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin17]
2019-06-15T07:14:53.426Z 47524 TID-ov7xxfh88 INFO: See LICENSE and the LGPL-3.0 for licensing details.
2019-06-15T07:14:53.426Z 47524 TID-ov7xxfh88 INFO: Upgrade to Sidekiq Pro for more features and support: http://sidekiq.org
2019-06-15T07:14:53.429Z 47524 TID-ov7xxfh88 ERROR: Your Redis configuration uses the namespace 'sidekiq' but the redis-namespace gem is not included in the Gemfile.Add the gem to your Gemfile to continue using a namespace. Otherwise, remove the namespace parameter.

どうやら、redis-namespace をインストールする必要があるようです。

redis-namespace をインストール

Gemfile

gem 'redis-namespace'
$ bundle install

設定ファイルの修正

キューを入れるときの設定も追記します。

※ namespace も myapp に変えました。

config/initializers/sidekiq.rb

Sidekiq.configure_server do |config|
  config.redis = { url: 'redis://localhost:6379', namespace: 'myapp' }
end

Sidekiq.configure_client do |config|
  config.redis = { url: 'redis://localhost:6379', namespace: 'myapp' }
end

rails と redis をそれぞれ再起動したら、OKです。

参考