pandas公式サイトのデータ構造のセクションを和訳してみました
pandasの使い方を調べるためにpandas公式サイトの英語のドキュメントを読みました。
英語の知識が大学受験時代で止まっている僕にとって英語のドキュメントを読むことは大変な苦行だったので、みなさんが同じ思いをしないように和訳をしてみました。
今回はpandasのデータ構造のセクションの途中までを和訳しています。(かなり意訳してるので、不自然なところや間違っているところがあればご指摘いただけるとうれしいです)
以下、和訳です。
データ構造入門
pandasの基本的なデータ構造の概要から始めましょう
データの型、index、ラベル付けについての基本的な振る舞いは全てのオブジェクトに対して適用されます。
では、始めるためにnumpyとpandasをimportしましょう
import numpy as np import pandas as pd
ここで、 データの整列機能は備わっている という基本的な考え方を覚えておいてください。
ラベルとデータ間の関連はあなたが明示的に破壊しない限り、壊れることはありません。
これからデータ構造について手短に説明し、それから多様な機能とメソッドについて別々のセクションで説明します。
1. Series
Seriesはあらゆる型(整数値, 文字列, 浮動小数点, pythonオブジェクト等)を格納可能な一次元の配列です。
ラベルはindexとみなされます。
Seriesを生成する基本的なメソッドは次です。
s = pd.Series(data, index=index)
dataには以下のように異なるデータを入れることが可能です。
indexはラベルのリストです。
そして、dataに何が入るかによってindexは以下のように複数のケースにわけられます。
ndarrayから生成する場合
dataがndarrayの場合、indexはdataの長さと同じでなければなりません。
もしindexが渡されなかったら、[0, ..., len(data) - 1]の値をラベルとして持つSeriesが生成されます。
>>>s = pd.Series(np.random.randn(5), index=['a', 'b', 'c', 'd', 'e']) >>>s """ a 0.2735 b 0.6052 c -0.1692 d 1.8298 e 0.5432 dtype: float64 """ >>>s.index """ Index([u'a', u'b', u'c', u'd', u'e'], dtype='object') """ >>>pd.Series(np.random.randn(5)) """ 0 0.3674 1 -0.8230 2 -1.0295 3 -1.0523 4 -0.8502 dtype: float64 """
pythonのdictから生成する場合
dataがdictでindexが指定された場合、indexのラベルに合わせて一致するdataの値が使用されます。
そうでなければdictのキーをソートしてindexを構築します。
>>>d = {'a' : 0., 'b' : 1., 'c' : 2.} >>>pd.Series(d) """ a 0.0 b 1.0 c 2.0 dtype: float64 """ >>>pd.Series(d, index=['b', 'c', 'd', 'a']) """ b 1.0 c 2.0 d NaN a 0.0 dtype: float64 """
スカラー値から生成する場合
dataがスカラー値の場合、indexは必須です。 値はindexの長さに合うように繰り返されます。
>>>pd.Series(5., index=['a', 'b', 'c', 'd', 'e']) """ a 5.0 b 5.0 c 5.0 d 5.0 e 5.0 dtype: float64 """
Seriesはndarrayライク
Seriesはndarrayに非常に似た振る舞いをし、NumPyの関数のほとんどに対して有効な引数として利用できます。しかしながら、スライスしたものはindexもスライスします。
>>>s[0] """ 0.27348116325673794 """ >>>s[:3] """ a 0.2735 b 0.6052 c -0.1692 dtype: float64 """ >>>s[s > s.median()] """ b 0.6052 d 1.8298 dtype: float64 """ >>>s[[4, 3, 1]] """ e 0.5432 d 1.8298 b 0.6052 dtype: float64 """ >>>np.exp(s) """ a 1.3145 b 1.8317 c 0.8443 d 6.2327 e 1.7215 dtype: float64 """
Seriesはdictライク
Seriesはindexに指定したラベルを使って値をセットしたり取得したりできる固定長のdictのようなものです。
>>>s['a'] """ 0.27348116325673794 """ >>>s['e'] = 12. >>>s """ a 0.2735 b 0.6052 c -0.1692 d 1.8298 e 12.0000 dtype: float64 """ >>>'e' in s """ True """ >>>'f' in s """ False """
存在しないラベルを指定した場合、例外が投げられます
>>> s['f'] KeyError: 'f'
getメソッドを使用することで、存在しないラベルを指定した場合に何も返却しない or 指定したデフォルト値を返却するようになります。
>>>s.get('f') >>>s.get('f', np.nan) """ nan """
Seriesのベクトル化とラベル付け
データ分析をするとき、NumPy配列が全要素に対して操作できるのと同様に、Seriesも値毎にループすることは通常必要ありません。
また、ndarrayが引数として渡されることを期待するNumPyメソッドのほとんどに、Seriesも渡すことができます。
>>>s + s """ a 0.5470 b 1.2104 c -0.3385 d 3.6596 e 24.0000 dtype: float64 """ >>>s * 2 """ a 0.5470 b 1.2104 c -0.3385 d 3.6596 e 24.0000 dtype: float64 """ >>>np.exp(s) """ a 1.3145 b 1.8317 c 0.8443 d 6.2327 e 162754.7914 dtype: float64 """
Seriesとndarrayの決定的な違いは、Series同士の操作は自動的にラベルを基にデータを並べて適用されることです。
以下のように、Seriesが同じラベルを持つかどうか考慮することなく、計算処理を記述することができます。
>>>s[1:] + s[:-1] """ a NaN b 1.2104 c -0.3385 d 3.6596 e NaN dtype: float64 """
並べ替えていないSeries同士の操作の結果は、indexを結合したものになります。
もし一方のSeriesにラベルが見つからなかった場合、結果はNaNになります。
明示的にデータを並べ替えずにコードをかけることは、対話型データ解析や調査に大きな自由と柔軟性をもたらします。
pandasのデータ構造が持つ統合されたデータ整列の機能は、ラベル付けされたデータへの操作を提供する関連ツールの大部分とは区別されます。
Name属性
SeriesはName属性を持つことができます。
>>>s = pd.Series(np.random.randn(5), name='something') >>>s """ 0 1.5140 1 -1.2345 2 0.5666 3 -1.0184 4 0.1081 Name: something, dtype: float64 """ >>>s.name """ 'something' """
SeriesのName属性は多くの場合自動的に付与されます。
特にDataFrameの一次元を切り出した場合は、以下のようになります。
SeriesのName属性は、pandas.Series.rename()メソッドで変更することができます。
>>>s2 = s.rename("different") >>>s2.name """ 'different' """
2. DataFrame
DataFrameはカラム毎に異なる型を持つことができる二次元のラベル付けされたデータ構造です。
表計算ソフトやSQLのテーブル、Seriesオブジェクトをもつdictのようなものとみなせます。
一般的にもっとも使われるpandasのオブジェクトです。
Seriesのように、DataFrameも多くの異なる入力値を受け付けます。
- 一次元のndarray, lists, dict, SeriesのDictオブジェクト
- 二次元のndarrayオブジェクト
- 構造化されたdtypeを要素に持つndarrayオブジェクト
- Seriesオブジェクト
- 他のDataFrameオブジェクト
データに加え、オプションでindex(行のラベル)とcolumns(列のラベル)を渡すことができます。
もしindexとcolumnsのいずれか、もしくは両方を渡した場合、生成したDataFrameのそれらを自身で決定することができます。
以下のように、Seriesと指定してindexのdictオブジェクトはindexに一致しない全てのデータを破棄します。
もし軸のラベルが渡されなかった場合、共通のルールに基いたデータから構築されます。
Series、もしくはdictを持つdictから生成する場合
生成結果のindexはDataFrameに含まれるSeriesのindexを結合したものになります。
もしネストしたdictがあった場合、最初にSeriesに変換されます。
もしcolumnsが渡されなかった場合、columnsはdictのキーをソートしたものになります。
>>>d = {'one' : pd.Series([1., 2., 3.], index=['a', 'b', 'c']), 'two' : pd.Series([1., 2., 3., 4.], index=['a', 'b', 'c', 'd'])} >>>df = pd.DataFrame(d) >>>df """ one two a 1.0 1.0 b 2.0 2.0 c 3.0 3.0 d NaN 4.0 """ >>>pd.DataFrame(d, index=['d', 'b', 'a']) """ one two d NaN 4.0 b 2.0 2.0 a 1.0 1.0 """ >>>pd.DataFrame(d, index=['d', 'b', 'a'], columns=['two', 'three']) """ two three d 4.0 NaN b 2.0 NaN a 1.0 NaN """
行と列のラベルは、それぞれindex属性とcolumns属性にアクセスすることで参照できます。
>>>df.index """ Index([u'a', u'b', u'c', u'd'], dtype='object') """ >>>df.columns """ Index([u'one', u'two'], dtype='object') """
ndarray、もしくはlistを持つdictから生成する場合★
ndarrayはすべて同じ長さでなければなりません。indexを渡す場合も、明らかに配列と同じ長さである必要があるます。
indexが渡されない場合、結果は配列の長さの範囲内になります。
>>>d = {'one' : [1., 2., 3., 4.], 'two' : [4., 3., 2., 1.]} >>>pd.DataFrame(d) """ one two 0 1.0 4.0 1 2.0 3.0 2 3.0 2.0 3 4.0 1.0 """ >>>pd.DataFrame(d, index=['a', 'b', 'c', 'd']) """ one two a 1.0 4.0 b 2.0 3.0 c 3.0 2.0 d 4.0 1.0 """
構造化されたdtypeを要素に持つ配列から生成する場合
この場合は配列のdictと全く同様に扱われます。
>>>data = np.zeros((2,), dtype=[('A', 'i4'),('B', 'f4'),('C', 'a10')]) >>>data[:] = [(1,2.,'Hello'), (2,3.,"World")] >>>pd.DataFrame(data) """ A B C 0 1 2.0 Hello 1 2 3.0 World """ >>>pd.DataFrame(data, index=['first', 'second']) """ A B C first 1 2.0 Hello second 2 3.0 World """ >>>pd.DataFrame(data, columns=['C', 'A', 'B']) """ C A B 0 Hello 1 2.0 1 World 2 3.0 """
dictのリストから生成する場合
>>>data2 = [{'a': 1, 'b': 2}, {'a': 5, 'b': 10, 'c': 20}] >>>pd.DataFrame(data2) """ a b c 0 1 2 NaN 1 5 10 20.0 """ >>>pd.DataFrame(data2, index=['first', 'second']) """ a b c first 1 2 NaN second 5 10 20.0 """ >>>pd.DataFrame(data2, columns=['a', 'b']) """ a b 0 1 2 1 5 10 """
tupleのdictから生成する場合
tupleのディクショナリを渡すことで、自動的に複数のindexを持つframeを生成することができます。
>>>pd.DataFrame({('a', 'b'): {('A', 'B'): 1, ('A', 'C'): 2}, ...('a', 'a'): {('A', 'C'): 3, ('A', 'B'): 4}, ...('a', 'c'): {('A', 'B'): 5, ('A', 'C'): 6}, ...('b', 'a'): {('A', 'C'): 7, ('A', 'B'): 8}, ...('b', 'b'): {('A', 'D'): 9, ('A', 'B'): 10}}) """ a b a b c a b A B 4.0 1.0 5.0 8.0 10.0 C 3.0 2.0 6.0 7.0 NaN D NaN NaN NaN NaN 9.0 """
Seriesから生成する場合
入力値に使用したSeriesのindexと同じindexを持ち、Seriesの元の名前と同じ名前を持つカラムを持ったDataFrameが生成される(ただし、他にカラム名が提供されていない場合に限る)
Missing Data
Much more will be said on this topic in the Missing data section. To construct a DataFrame with missing data, use np.nan for those values which are missing. Alternatively, you may pass a numpy.MaskedArray as the data argument to the DataFrame constructor, and its masked entries will be considered missing.
選択可能なコンストラクタ
DataFrame.from_dict
DataFrame.from_dictは、dictのdictもしくは配列のようなシーケンスなデータ構造のdictを引数にとり、DataFrameを返却します。 これは方向に関するパラメータ以外DataFrameのコンストラクタの用に振る舞います。方向に関するパラメータはデフォルトではcolumnsになりますが、indexに設定してdictのキーを行のラベルとして使うことができます。
DataFrame.from_records
DataFrame.from_recordsはtupleのリスト、もしくは構造化されたdtypeを型に持つndarrayを引数にとります。
このメソッドは普通のDataFrameのコンストラクタと同じように働きます。ただし、dtype内の指定したフィールドの値をindexにできる点が異なります。
例は以下:
>>>data """ array([(1, 2.0, 'Hello'), (2, 3.0, 'World')], dtype=[('A', '<i4'), ('B', '<f4'), ('C', 'S10')]) """ >>>pd.DataFrame.from_records(data, index='C') """ A B C Hello 1 2.0 World 2 3.0 """
DataFrame.from_items
DataFrame.from_itemsはキーバリューのペアの集まりを引数にとるdictのコンストラクタと同様に働き、キーは列(orient='index'のように指定した場合は行)の名前になり、値は列の値(もしくは行の値)になります。
これは明示的に列のリストを渡すことなく、特定の順序のカラムを持ったDataFrameを構築するのに便利です。
>>>pd.DataFrame.from_items([('A', [1, 2, 3]), ('B', [4, 5, 6])]) """ A B 0 1 4 1 2 5 2 3 6 """
orient='index'を渡した場合、キーは行のラベルになります。
しかし、この場合は希望する列名も渡さなければなりません。
>>>pd.DataFrame.from_items([('A', [1, 2, 3]), ('B', [4, 5, 6])], ...orient='index', columns=['one', 'two', 'three']) """ one two three A 1 2 3 B 4 5 6 """
列の追加、削除
DataFrameは意味的にSeriesオブジェクトのdictのように扱うことができます。列の取得・設定・削除はdictの操作と同様の文法で動きます。
>>>df['one'] """ a 1.0 b 2.0 c 3.0 d NaN Name: one, dtype: float64 """ >>>df['three'] = df['one'] * df['two'] >>>df['flag'] = df['one'] > 2 >>>df """ one two three flag a 1.0 1.0 1.0 False b 2.0 2.0 4.0 False c 3.0 3.0 9.0 True d NaN 4.0 NaN False """
列はdictのように削除・ポップすることが可能です。
>>>del df['two'] >>>three = df.pop('three') >>>df """ one flag a 1.0 False b 2.0 False c 3.0 True d NaN False """
スカラー値を挿入するときは、挿入した列全体がそれで満たされます。
>>>df['foo'] = 'bar' >>>df """ one flag foo a 1.0 False bar b 2.0 False bar c 3.0 True bar d NaN False bar """
DataFrameと同じだけのindexを持っていないSeriesを挿入するときは、DataFrameのindexに従います。
>>>df['one_trunc'] = df['one'][:2] >>>df """ one flag foo one_trunc a 1.0 False bar 1.0 b 2.0 False bar 2.0 c 3.0 True bar NaN d NaN False bar NaN """
素のndarrayを挿入することができますが、それらの長さはDataFrameのindexの長さと一致していなければなりません。
デフォルトでは、列は最後に挿入されます。insert()メソッドを使って列の特定の場所に挿入することができます。
>>>df.insert(1, 'bar', df['one']) #1列目にbar列として、df['one']を挿入 >>>df """ one bar flag foo one_trunc a 1.0 1.0 False bar 1.0 b 2.0 2.0 False bar 2.0 c 3.0 3.0 True bar NaN d NaN NaN False bar NaN """
途中ですが、今回はここまで。
続きは別の機会に和訳していきたいと思います!