ためすう
pandas で データ型を確認する
2019-06-29やったこと
pandas のデータ構造である
- Series
- DataFrame
について調べたことを書きます。
確認環境
$ 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
調査
DataFrame の各列の型を確認してみます。
data = {'state': ['Ohio', 'Nevada'],
'year': [2000, 2001]}
df = DataFrame(data)
df.dtypes
出力結果
state object
year int64
dtype: object
参考
第12章 唯一を保証する: Singleton
2019-06-29「Rubyによるデザインパターン」の読書メモです。
ただ1つのインスタンスしか持てないクラスで、その1つのインスタンスへのアクセスをグローバルに提供します。
シングルトンを使わないロガーの例
test.rb
class SimpleLogger
attr_accessor :level
ERROR = 1
WARNING = 2
INFO = 3
def initialize
@level = WARNING
end
def error(msg)
p "#{self.class}##{__method__}"
p msg
end
def warning(msg)
p "#{self.class}##{__method__}" if @level >= WARNING
p msg
end
def info(msg)
p "#{self.class}##{__method__}" if @level >= INFO
p msg
end
end
logger = SimpleLogger.new
logger.level = SimpleLogger::INFO
logger.info('1つめ')
logger.info('2つめ')
logger2 = SimpleLogger.new
p '---'
p logger.object_id
p logger2.object_id
出力結果
$ ruby test.rb
"SimpleLogger#info"
"1つめ"
"SimpleLogger#info"
"2つめ"
"---"
70311664013160
70311664012640
シングルトンを適用したロガーの例
test.rb
class SimpleLogger
attr_accessor :level
@@instance = SimpleLogger.new
ERROR = 1
WARNING = 2
INFO = 3
def initialize
@level = WARNING
end
def error(msg)
p "#{self.class}##{__method__}"
p msg
end
def warning(msg)
p "#{self.class}##{__method__}" if @level >= WARNING
p msg
end
def info(msg)
p "#{self.class}##{__method__}" if @level >= INFO
p msg
end
def self.instance
return @@instance
end
private_class_method :new
end
logger = SimpleLogger.instance
logger.level = SimpleLogger::INFO
logger.info('1つめ')
logger.info('2つめ')
logger2 = SimpleLogger.instance
p '---'
p logger.object_id
p logger2.object_id
出力結果
$ ruby test.rb
"SimpleLogger#info"
"1つめ"
"SimpleLogger#info"
"2つめ"
"---"
70234052873820
70234052873820
シングルトンモジュール
Singleton モジュールを使って、実装することができます。
include Singleton
シングルトンとしてのクラス
class ClassBasedLogger
ERROR = 1
WARNING = 2
INFO = 3
def self.error(msg)
p "#{self.class}##{__method__}"
p msg
end
end
シングルトンとしてのモジュール (自作)
module ClassBasedLogger
ERROR = 1
WARNING = 2
INFO = 3
def self.error(msg)
p "#{self.class}##{__method__}"
p msg
end
end
モジュールは、インスタンス化するためにあるのではないと少しは示すことができます。
Need-to-Know原則に基づくシングルトン
Need-to-Know原則とは > 「必要な人にだけ情報を開示する」という情報セキュリティにおける基本的な考え方です。
require 'singleton'
class DatabaseConnectionManager
include Singleton
def get_connection
p "#{self.class}##{__method__}"
end
end
class PreferenceManager
def initialize
@reader = PrefReader.new
@writer = PrefWriter.new
@preferences = {
:display_splash => false,
:background_color => :blue
}
end
def save_preferences
@writer.write(DatabaseConnectionManager.instance, @preferences)
end
def get_preferences
@preferences = @reader.read(DatabaseConnectionManager.instance)
end
end
class PrefWriter
def write(manager, preferences)
p "#{self.class}##{__method__}"
p preferences
manager.get_connection
end
end
class PrefReader
def read(manager)
p "#{self.class}##{__method__}"
manager.get_connection
p '読み取ったもの'
end
end
PreferenceManager.new.get_preferences
PreferenceManager.new.save_preferences
シングルトンは実装詳細なので、シングルトンであると知っているクラスを少なくした方が良いようです。
第11章 オブジェクトを改良する: Decorator
2019-06-27「Rubyによるデザインパターン」の読書メモです。
Decoratorパターンについて
既存のオブジェクトに対して簡単に機能を追加するためのパターンです。Decoratorパターンを使うとレイヤ状に機能を積み重ねていくことができ、それぞれの状況でまさに必要なだけの機能をもつオブジェクトを作ることができます。
今回、ファイルに下記の形式で書き出すことができるクラスを作ります。
- プレーンテキスト
- チェックサム付き
- タイムスタンプ付き
- 行番号付き
継承ベースのアプローチ
継承ベースのアプローチでは、あり得るすべての機能の組み合わせを前もって設計時に考えなければならない,ということです。
クラスの数が増えすぎるので、不採用とします。
Decoratorパターンで実装した例
class SimpleWriter
def initialize(path)
@file = File.open(path, 'w')
end
def write_line(line)
@file.print(line)
@file.print("\n")
end
def pos
@file.pos
end
def rewind
@file.rewind
end
def close
@file.close
end
end
class WriterDecorator
def initialize(real_writer)
@real_writer = real_writer
end
def write_line(line)
@real_writer.write_line(line)
end
def pos
@real_writer.pos
end
def rewind
@real_writer.rewind
end
def close
@real_writer.close
end
end
class NumberingWriter < WriterDecorator
def initialize(real_writer)
super(real_writer)
@line_number = 1
end
def write_line(line)
@real_writer.write_line("#{@line_number}: #{line}")
@line_number += 1
end
end
class CheckSummingWriter < WriterDecorator
attr_reader :check_sum
def initialize(real_writer)
@real_writer = real_writer
@check_sum = 0
end
def write_line(line)
line.each_byte {|byte| @check_sum = (@check_sum + byte) % 256}
# check_sum の計算...
@real_writer.write_line("#{line} (checksum: #{@check_sum})")
end
end
class TimeStampingWriter < WriterDecorator
def write_line(line)
@real_writer.write_line("#{Time.new}: #{line}")
end
end
writer = CheckSummingWriter.new(
TimeStampingWriter.new(
NumberingWriter.new(SimpleWriter.new('final.txt'))
)
)
writer.write_line('Hello out there')
出力された final.txt
1: 2019-06-23 13:28:36 +0900: Hello out there (checksum: 164)
Forwardable を使った例
class SimpleWriter
def initialize(path)
@file = File.open(path, 'w')
end
def write_line(line)
@file.print(line)
@file.print("\n")
end
def pos
@file.pos
end
def rewind
@file.rewind
end
def close
@file.close
end
end
出力された final2.txt
2019-06-23 13:34:25 +0900: Hello out there
ラッピングする例
class SimpleWriter
def initialize(path)
@file = File.open(path, 'w')
end
def write_line(line)
@file.print(line)
@file.print("\n")
end
def pos
@file.pos
end
def rewind
@file.rewind
end
def close
@file.close
end
end
w = SimpleWriter.new('final3.txt')
class << w
alias old_write_line write_line
def write_line(line)
old_write_line("#{Time.new}: #{line}")
end
end
w.write_line('write_line')
w.old_write_line('old_write_line')
出力された final3.txt
2019-06-23 13:42:53 +0900: write_line
old_write_line
モジュールを使った例
class SimpleWriter
def initialize(path)
@file = File.open(path, 'w')
end
def write_line(line)
@file.print(line)
@file.print("\n")
end
def pos
@file.pos
end
def rewind
@file.rewind
end
def close
@file.close
end
end
module TimeStampingWriter
def write_line(line)
super("#{Time.new}: #{line}")
end
end
w = SimpleWriter.new('final4.txt')
w.extend(TimeStampingWriter)
w.write_line('hello')
出力された final4.txt
2019-06-23 13:45:02 +0900: hello
第10章 オブジェクトに代理を立てる: Proxy part2
2019-06-27「Rubyによるデザインパターン」の読書メモです。
Proxy で重複を取り除いたパターン
test.rb
class BankAccount
attr_reader :balance
def initialize(starting_balance=0)
@balance = starting_balance
end
def deposit(amount)
@balance += amount
end
def withdraw(amount)
@balance -= amount
end
end
class AccountProxy
def initialize(real_account)
@subject = real_account
end
def method_missing(name, *args)
p "Delegating #{name} message to subject."
@subject.send(name, *args)
end
end
ap = AccountProxy.new(BankAccount.new(100))
ap.deposit(25)
ap.withdraw(50)
p ap.balance
出力結果
$ ruby test.rb
"Delegating deposit message to subject."
"Delegating withdraw message to subject."
"Delegating balance message to subject."
75
method_missing をオーバーライドして、委譲メソッドを別々に定義しなくても良くなりました。
Proxy パターンの注意
method_missingの使いすぎは、継承の使いすぎと同様、あなたのコードをわかりにくくしてしまう代表的な原因になるということです。
次に来る実装者には、実装により意図が伝わるようにしてください。
pandas の head、tail を使ってみる
2019-06-26やったこと
pandas で DataFrame のデータを確認してみます。
確認環境
$ 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")
head を使ってみる
デフォルトだと先頭から5行出力されます。
train.head()
tail を使ってみる
デフォルトだと最終行から5行出力されます。
train.tail()
参考
第10章 オブジェクトに代理を立てる: Proxy part1
2019-06-26「Rubyによるデザインパターン」の読書メモです。
Proxy パターンは下記の問題の解決策となります。
- オブジェクトへのアクセス制御
- 場所に依存しないオブジェクトの取得方法の提供
- オブジェクト生成の遅延
プロキシーが気にするのは、何かをすることが許されているのは誰で、許されていないのが誰なのかということです。
Proxy パターンの例
3つの問題について、解決させるように試みたコードです。
test.rb
class BankAccount
attr_reader :balance
def initialize(starting_balance=0)
@balance = starting_balance
end
def deposit(amount)
@balance += amount
end
def withdraw(amount)
@balance -= amount
end
end
require 'etc'
class BankAccountProxy
def initialize(starting_balance, owner_name, &creation_block)
@starting_balance = starting_balance
@owner_name = owner_name
@creation_block = creation_block
end
def balance
check_access
subject.balance
end
def deposit(amount)
check_access
subject.deposit(amount)
end
def withdraw(amount)
check_access
subject.withdraw(amount)
end
def check_access
if Etc.getlogin != @owner_name
raise "Illegal access: #{Etc.getlogin} cannot access account."
end
end
def subject
@subject || (@subject = @creation_block.call)
end
end
account = BankAccount.new(100)
account.deposit(50)
account.withdraw(10)
p account.balance
account2 = BankAccountProxy.new(account.balance, 'hogehoge') { BankAccount.new(300) }
account2.deposit(50)
account2.withdraw(10)
p account2.balance
出力結果
$ ruby test.rb
140
340
第9章 ギャップを埋める: Adapter
2019-06-26「Rubyによるデザインパターン」の読書メモです。
アダプタは既存のインターフェースと必要なインターフェースとの間の深い溝を橋渡しするオブジェクトです。
Adapter パターンの例
例えば、TextObject を表示する実装が既にあって、
フォーマットが異なる BritishTextObject に手を入れることができない場合にも
BritishTextObject を扱うことができます。
class Renderer
def render(text_object)
text = text_object.text
size = text_object.size_inches
color = text_object.color
p text
p size
p color
end
end
class TextObject
attr_reader :text, :size_inches, :color
def initialize(text, size_inches, color)
@text = text
@size_inches = size_inches
@color = color
end
end
class BritishTextObject
attr_reader :string, :size_mm, :colour
def initialize(string, size_mm, colour)
@string = string
@size_mm = size_mm
@colour = colour
end
end
class BritshTextObjectAdapter < TextObject
def initialize(bto)
@bto = bto
end
def text
@bto.string
end
def size_inches
@bto.size_mm / 4
end
def color
@bto.colour
end
end
t = TextObject.new('text2', 100, 'blue')
Renderer.new.render(t)
b = BritishTextObject.new('string', 300, 'green')
bto = BritshTextObjectAdapter.new(b)
Renderer.new.render(bto)
Ruby でクラス拡張するパターン
class Renderer
def render(text_object)
text = text_object.text
size = text_object.size_inches
color = text_object.color
p text
p size
p color
end
end
class TextObject
attr_reader :text, :size_inches, :color
def initialize(text, size_inches, color)
@text = text
@size_inches = size_inches
@color = color
end
end
class BritishTextObject
attr_reader :string, :size_mm, :colour
def initialize(string, size_mm, colour)
@string = string
@size_mm = size_mm
@colour = colour
end
end
# 上記 BritishTextObject は require されているつもり
# 拡張
class BritishTextObject
def text
string
end
def size_inches
size_mm / 4
end
def color
colour
end
end
t = TextObject.new('text2', 100, 'blue')
Renderer.new.render(t)
b = BritishTextObject.new('string', 300, 'green')
Renderer.new.render(b)
まとめ
繰り返しますが、コードはパターンにとってそれほど重要ではないということを覚えておいてください。意図が重要です。不適切なインターフェースを持つオブジェクトに困っていて、不適切なインターフェースを扱う痛みがシステム中に広がることを防ぎたい場合に限り、アダプタを選んでください。
第8章 命令を実行する: Command
2019-06-25「Rubyによるデザインパターン」の読書メモです。
コマンドをクラスにするパターン
class SlickButton
attr_accessor :command
def initialize(command)
@command = command
end
def on_button_push
@command.execute if @command
end
end
class SaveCommand
def execute
p "#{self.class}##{__method__}"
end
end
save_button = SlickButton.new(SaveCommand.new)
save_button.on_button_push
コマンドをコードブロックにするパターン
コマンドは何か特定のことを行うコードの単純なラッパーで、唯一の存在理由は、適切なタイミングでコードを実行することです。
class SlickButton
attr_accessor :command
def initialize(&block)
@command = block
end
def on_button_push
@command.call if @command
end
end
new_button = SlickButton.new do
p "#{self.class}"
end
new_button.on_button_push
記録 + 戻るの例
class Command
attr_reader :description
def initialize(description)
@description = description
end
def execute
end
def unexecute
end
end
class CreateFile < Command
def initialize(path, contents)
super("Create file: #{path}")
@path = path
@contents = contents
end
def execute
p "#{self.class}##{__method__}"
end
def unexecute
p "#{self.class}##{__method__}"
end
end
class DeleteFile < Command
def initialize(path)
super("Delete file: #{path}")
@path = path
end
def execute
p "#{self.class}##{__method__}"
end
def unexecute
p "#{self.class}##{__method__}"
end
end
class CopyFile < Command
def initialize(source, target)
super("Copy file: #{source} to #{target}")
@source = source
@target = target
end
def execute
p "#{self.class}##{__method__}"
end
def unexecute
p "#{self.class}##{__method__}"
end
end
class CompositeCommand < Command
def initialize
@commands = []
end
def add_command(cmd)
@commands << cmd
end
def execute
@commands.each {|cmd| cmd.execute}
end
def unexecute
@commands.reverse.each {|cmd| cmd.unexecute}
end
def description
description = ''
@commands.each {|cmd| description += cmd.description + "\n"}
description
end
end
cmds = CompositeCommand.new
cmds.add_command(CreateFile.new('file1.txt', 'hello world'))
cmds.add_command(CopyFile.new('file1.txt', 'file2.txt'))
cmds.add_command(DeleteFile.new('file1.txt'))
puts cmds.description
p '---'
cmds.execute
p '---'
cmds.unexecute
注意
Commandパターンのポイントは何を行うかの決定と、それの実行とを分離することです。
まとめ
Commandパターンでは、ある特定の動作を実行するオブジェクトを構築します。「特定」という部分が重要です。
第7章 コレクションを操作する: Iterator
2019-06-25「Rubyによるデザインパターン」の読書メモです。
時々、個人の見解入りです。
集約オブジェクトがもとにある内部表現を公開せずに、その要素に順にアクセスする方法を提供する
外部イテレータ
class ArrayIterator
def initialize(array)
@array = array
@index = 0
end
def has_next?
@index < @array.length
end
def item
@array[@index]
end
def next_item
value = @array[@index]
@index += 1
value
end
end
array = ['red','green', 'blue']
i = ArrayIterator.new(array)
while i.has_next?
p "item: #{i.next_item}"
end
内部イテレータ
def for_each_element(array)
i = 0
while i < array.length
yield(array[i])
i += 1
end
end
a = [10, 20, 30]
for_each_element(a) { |element| p "The element is #{element}" }
外部イテレータと内部イテレータ
外部イテレータはクライアントが繰り返しを制御します。
内部イテレータは始めから終わりまで実行されます。
Enumerable モジュールを使う
class Account
attr_accessor :name, :balance
def initialize(name, balance)
@name = name
@balance = balance
end
def <=>(other)
balance <=> other.balance
end
end
class Portfolio
include Enumerable
def initialize
@accounts = []
end
def each(&block)
@accounts.each(&block)
end
def add_account(account)
@accounts << account
end
end
a1 = Account.new('account1', 200)
a2 = Account.new('account2', 400)
a3 = Account.new('account3', 600)
portfolio = Portfolio.new
arr = [a1, a2, a3]
arr.each do |a|
portfolio.add_account(a)
end
p portfolio.any? {|account| account.balance > 500}
p portfolio.all? {|account| account.balance > 500}
コードブロックで判定条件を渡せます。
注意点
test.rb
array = ['red', 'green', 'blue', 'purple']
array.each do |color|
p color
if color == 'green'
array.delete(color)
end
end
出力結果
$ ruby test.rb
"red"
"green"
"purple"
blue がスキップされてしまいました
test.rb
def change_resistant_for_each_element(array)
copy = Array.new(array)
i = 0
while i < copy.length
yield(copy[i])
i += 1
end
end
array = ['red', 'green', 'blue', 'purple']
change_resistant_for_each_element(array){|color|
p color
if color == 'green'
array.delete(color)
end
}
配列を削除しても、全部捜査されました。
出力結果
$ ruby test.rb
"red"
"green"
"blue"
"purple"
pandas で 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
調査
to_csv を利用して csv ファイルを作成します。
引数の index は、出力したファイルに index を含むかどうかです。
data = {'state': ['Ohio', 'Nevada'],
'year': [2000, 2001]}
df = DataFrame(data)
df.to_csv("hoge.csv", index=False)
出力された hoge.csv
state,year
Ohio,2000
Nevada,2001