け日記

SIerから転職したWebアプリエンジニアが最近のIT技術キャッチアップに四苦八苦するブログ

Python PostgreSQLのテーブルをPandasのDataFrameへ読み込む

PostgreSQLのテーブルをPandasのDataFrameに読み込む方法の備忘録です。

今回も以下で提供されているサンプルデータを使っています。
PostgreSQL Sample Database

まずはPandasと、PostgreSQLのドライバとしてpsycopg2をインポートして、DBに接続しています。

import pandas as pd
import psycopg2

# 接続情報
connection_config = {
    'host': 'localhost',
    'port': '5432',
    'database': 'test',
    'user': 'ohke',
    'password': 'ohke'
}

# 接続
connection = psycopg2.connect(**connection_config)

次にDataFrameへロードします。 Pandasのread_sqlメソッドに先程接続したコネクションとSQL(文字列)を渡すことで、SELECT結果をDataFrameとして返してくれます。

引数index_colでインデックスを指定しています。 この引数が無い場合は、1から自動的に採番されます。

# DataFrameでロード
rentals = pd.read_sql(sql="SELECT * FROM rental;", con=connection, index_col='rental_id' )

# 表示
rentals

Jupyterでは以下のように表形式で表示されます。 テーブルのカラム名がそのままDataFrameのカラム名として使われていること、インデックスとしてrental_idが使われていることが確認できます。

f:id:ohke:20170623085109p:plain

普通のDataFrameですので、例えばカラム名でSeriesを取り出すこともできます。

rentals['inventory_id']
rental_id
2        1525
3        1711
4        2452
5        2079
6        2792
7        3995
8        2346
9        2580

時刻はTimestamp型にマッピングされるようです。

rentals['rental_date'][1]
Timestamp('2005-05-24 22:53:30')

また、統計値も取得できます(あまり意味のある値ではありませんが)。

rentals.describe()

f:id:ohke:20170623090607p:plain:w350

C# HttpClientでKeep-Aliveを無効にする

仕事で利用していたクラウドサービスのAPIの仕様で、リクエスト都度で認証する必要があり、ハマったので備忘録にしておきます。

当初は以下のようにHttpClientでAPIをコールしていたのですが、一度認証されるとそのセッションが使いまわされてしまいます。
HttpClientをDisposeすることでもコネクションを破棄することもできるのですが、それはそれでソケットの枯渇という別の問題も生みます。

開発者を苦しめる.NETのHttpClientのバグと紛らわしいドキュメント

// HttpClientインスタンスはstatic変数として使いまわす
private static HttpClient httpClient = new HttpClient()

// リクエスト生成
var request = new HttpRequestMessage
{
    Method = HttpMethod.Post,
    RequestUri = new Uri("http://ohke.hateblo.jp/")
};

// Basic認証ヘッダを付与する
request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue(
    "Basic",
    Convert.ToBase64String(Encoding.ASCII.GetBytes(string.Format("{0}:{1}", userName, userPassword))));

// リクエストの送信
var response = await httpClient.SendAsync(request);

リクエスト都度認証させるためにはKeep-Aliveを無効にして、HTTPのConnectionヘッダをCloseにする必要があります(これに気付かされるのも時間がかかったのですが)。

HttpWebRequestにはKeepAliveプロパティをfalseにすれば良いのですが、HttpClientを使う場合はHttpRequestMessageのHeadersプロパティ(またはHttpClientのDefaultRequestHeaders)に直接書き加える必要があります。

これでリクエストヘッダがConnection: Closeとなり、Keep-Aliveが無効になります。

// Basic認証ヘッダを付与する
request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue(
    "Basic",
    Convert.ToBase64String(Encoding.ASCII.GetBytes(string.Format("{0}:{1}", userName, userPassword))));

// Keep-Aliveをオフにする(リクエスト都度認証させる)
request.Headers.Add("Connection", "close");

// リクエストの送信
var response = await httpClient.SendAsync(request);

通常は一度認証したセッションは使いまわすべきなので、レアケースかとは思います。

LINQと同じ処理をPythonのリストで行う

最近、本腰を入れてPythonを基礎から勉強し直しています。 そうした中で、「LINQならああやって書くアレは、Pythonではどうやって書くのだろう?」と調べることが増えてきましたので、一旦整理しておきたいと思います。

今回はリストです。 辞書や集合などもまとめるかもです。

リストの生成(Range)

Enumerable.Rangeメソッドに対応する関数として、Pythonではrangeがあります。

# var numbers = Enumerable.Range(1, 5);
numbers = [number for number in range(1, 6)] # [1, 2, 3, 4, 5]

SelectとWhere

forの前で値を取得・変形することでSelectメソッド、if句で絞り込みでWhereメソッドと、それぞれ同じことができます。

組み合わせることで、Whereで絞った要素をSelectで取得するというのも簡単にできます。

# Select
# var squareNumbers = numbers.Select(i => i*i);
square_numbers = [i**2 for i in numbers] # [1, 4, 9, 16, 25]

# Where
# var oddNumbers = numbers.Where(i => i % 2 == 1);
odd_numbers = [i for i in numbers if i % 2 == 1] # [1, 3, 5]

# 組み合わせる
# var oddSquareNumbers = numbers.Where(i => i % 2 == 1).Select(i => i*i);
odd_square_numbers = [i**2 for i in numbers if i % 2 == 1] # [1, 9, 25]

TakeとSkip

内包表記の場合、enumerate関数でリストのインデックスと値の両方を同時に取得できます。 インデックスをif句で絞り込むことで、TakeやSkipメソッドと同じことができます。

ただ、単に「先頭から何番目まで」「末尾から何番目まで」という絞り込みだけであれば、添字指定の方が簡略で読みやすいです。

# Take
# var takeNumbers = numbers.Take(2);
take_numbers = [number for index, number in enumerate(numbers) if index < 2] # [1, 2]
# または
# take_numbers = numbers[0:2]

# Skip
# var skipNumbers = numbers.Skip(2);
skip_numbers = [number for index, number in enumerate(numbers) if index >= 2]
# または
# skip_numbers = numbers[2:]

# 組み合わせる
# var medianNumbers = numbers.Skip(2).Take(1);
median_numbers = [number for index, number in enumerate([number for index, number in enumerate(numbers) if index >= 2])  if index < 1] # 3
# または
# median_numbers = numbers[2:3]

FirstとLast

ポイントはifで絞り込んだ後に添字を指定することです。

# First
# var firstEvenNumber = numbers.First(i => i % 2 == 0);
first_even_number = [i for i in numbers if i % 2 == 0][0] # 2

# Last
# var lastEvenNumber = numbers.Last(i => i % 2 == 0);
last_even_number = [i for i in numbers if i % 2 == 0][-1] # 4

判定(Any/All/Contains)

判定用の関数としてanyとallが提供されています。 inを使うことでリスト中に同じ値があるかどうかを判定できます。

# Any
# var anyEven = numbers.Any(i => i % 2 == 0);
any_even = any([i % 2 == 0 for i in numbers]) # True

# All
# var allEven = number.All(i => i % 2 == 0);
all_even = all([i % 2 == 0 for i in numbers]) # False

# Contains
# var containsThree = numbers.Contains(3);
contains_three = 3 in numbers # True

順序(OrderBy/OrderByDescending)

リストの場合、sorted関数でデフォルトでは昇順ソートされ、引数reverseにTrueを渡すと降順でソートされます。 また、sortedの引数keyにラムダ式でソート方法を任意に設定することもできます。

# OrderBy
# var orderedNumbers = numbers.OrderBy(i => i);
ordered_numbers = sorted(numbers) # [1, 2, 3, 4, 5]

# OrderByDescending
# var disorderedNumbers = numbers.OrderByDescending(i => i);
disordered_numbers = sorted(numbers, reverse=True) # [5, 4, 3, 2, 1]

集約(Sum/Average/Min/Max)

sum、min、maxの集約関数が用意されているのでそれらを使うことで同じことができます。 Averageメソッドに対応するものはありませんが、sumの値をlenで除算することで簡単に導出できます。

# Sum
# var sumNumber = numbers.Sum(i => i);
sum_number = sum(numbers) # 15

# Average
# var averageNumber = numbers.Average(i => i);
average_number = sum(numbers) / len(numbers) # 3.0

# Min
# var minNumber = numbers.Min(i => i);
min_number = min(numbers) # 1

# Max
# var maxNumber = numbers.Max(i => i);
max_number = max(numbers) # 5

Zip

zip関数が提供されており、これを使うことで2つのリストの値を同時に取得できます。

# Zip
# var addedNumbers = numbers.Zip(squareNumbers, (first, second) => first + second);
added_numbers = [first + second for first, second in zip(numbers, square_numbers)] # [2, 6, 12, 20, 30]