pandas.DataFrameの構造とその作成方法

pandas.DataFrameは二次元の表形式のデータ(テーブルデータ)を表す、pandasの基本的な型。

DataFrame — pandas 0.24.2 documentation pandas.DataFrame() — pandas 0.24.2 documentation

ここではまずはじめにpandas.DataFrameの構造と基本操作について説明します。

pandas.DataFrameの構造 3つの構成要素: values, columns, index 列名columns, 行名indexの変更 行・列・要素の選択・抽出および変更 列ごとに様々な型を持つDataFrame

そのあとでコンストラクタpandas.DataFrame()による作成方法およびファイルからの読み込み方法について説明します。

二次元配列・リストからDataFrameを作成 複数の一次元配列・リストからDataFrameを作成 辞書のリスト・辞書からDataFrameを作成 CSVファイルやExcelファイルから読み込み

pandas関連の記事は以下のリンクから。

pandas.DataFrameの構造

3つの構成要素: values, columns, index

DataFrameはvalues, columns, indexの3つの要素から構成されています。 その名前の通り、valuesは実際のデータの値、columnsは列名(列ラベル)、indexは行名(行ラベル)。 最もシンプルなDataFrameは以下のようなもの。なおDataFrameの作成については後述。ここでは特に気にしなくてよい。

import pandas as pd
import numpy as np

df_simple = pd.DataFrame(np.arange(12).reshape(3, 4))

print(df_simple)
#    0  1   2   3
# 0  0  1   2   3
# 1  4  5   6   7
# 2  8  9  10  11

values, columns, indexはそのままDataFrameの属性としてアクセスできます。 valuesはNumPy配列ndarray。

print(df_simple.values)
# [[ 0  1  2  3]
#  [ 4  5  6  7]
#  [ 8  9 10 11]]

print(type(df_simple.values))
# <class 'numpy.ndarray'>

columnsとindexはここでは特に設定していないためRangeIndex型で単純な連番となっています。

print(df_simple.columns)
# RangeIndex(start=0, stop=4, step=1)

print(type(df_simple.columns))
# <class 'pandas.core.indexes.range.RangeIndex'>

print(df_simple.index)
# RangeIndex(start=0, stop=3, step=1)

print(type(df_simple.index))
# <class 'pandas.core.indexes.range.RangeIndex'>

RangeIndex型はlist()やtolist()メソッドでリスト化できます。

print(list(df_simple.columns))
# [0, 1, 2, 3]

print(type(list(df_simple.columns)))
# <class 'list'>

print(df_simple.columns.tolist())
# [0, 1, 2, 3]

print(type(df_simple.columns.tolist()))
# <class 'list'>

columns, indexを設定することで、各列・各行に任意の名前(ラベル)をつけることができます。

df = pd.DataFrame(np.arange(12).reshape(3, 4),
                  columns=['col_0', 'col_1', 'col_2', 'col_3'],
                  index=['row_0', 'row_1', 'row_2'])

print(df)
#        col_0  col_1  col_2  col_3
# row_0      0      1      2      3
# row_1      4      5      6      7
# row_2      8      9     10     11

このときのcolumnsとindexはIndex型。

print(df.columns)
# Index(['col_0', 'col_1', 'col_2', 'col_3'], dtype='object')

print(type(df.columns))
# <class 'pandas.core.indexes.base.Index'>

print(df.index)
# Index(['row_0', 'row_1', 'row_2'], dtype='object')

print(type(df.index))
# <class 'pandas.core.indexes.base.Index'>

Index型もlist()やtolist()メソッドでリスト化できます。

print(df.columns.tolist())
# ['col_0', 'col_1', 'col_2', 'col_3']

print(type(df.columns.tolist()))
# <class 'list'>

列名columns, 行名indexの変更

columnsとindexのIndex型は様々なメソッドを持っているが、基本的には行・列の名前(ラベル)が格納された配列だと考えてよい。 インデックス[]で要素を取得できるが、通常のリストやNumPy配列ndarrayと異なり、新たな値を代入して要素を変更することができない。

print(df.columns[0])
# col_0

# df.columns[0] = 'Col_0'
# TypeError: Index does not support mutable operations

columns, index属性に新しいリストや配列を設定することは可能。このときは通常のリストやndarrayをそのまま使える。Index型に変更したりする必要はない。

df.columns = ['Col_0', 'Col_1', 'Col_2', 'Col_3']

df.index = ['Row_0', 'Row_1', 'Row_2']

print(df)
#        Col_0  Col_1  Col_2  Col_3
# Row_0      0      1      2      3
# Row_1      4      5      6      7
# Row_2      8      9     10     11

一方、values属性は変更不可。新たな配列などを直接設定することはできない。valuesの要素の変更については次に説明します。

# df.values = np.arange(12).reshape(3, 4) * 10
# AttributeError: can't set attribute

上述のように、columnsとindexの要素をインデックスで指定して新たな値を代入することはできないが、rename()メソッドを使うと個別に変更することができます。以下の記事を参照。

より発展的な内容として、階層的なインデックスを持つことも可能。以下の記事を参照。

行・列・要素の選択・抽出および変更

上述のようにDataFrameのvalues属性は変更できないが、行や列、要素を選択して新たな値を代入することはできます。 行・列・要素の選択・抽出 df['列名']で列を選択できます。選択した列は一次元データを表すpandas.Seriesとなります。

print(df['Col_1'])
# Row_0    1
# Row_1    5
# Row_2    9
# Name: Col_1, dtype: int64

print(type(df['Col_1']))
# <class 'pandas.core.series.Series'>

属性のようにdf.列名として列を選択することも可能。ただし、列名がDataFrameのメソッドと同じ場合は使えないので注意が必要。

print(df.Col_1)
# Row_0    1
# Row_1    5
# Row_2    9
# Name: Col_1, dtype: int64

より一般的な行・列・要素の選択にはloc[]を使います。loc[行名, 列名]のように範囲を指定します。スライス:も使えるため、例えば:で全行を指定すると列が選択できます。

print(df.loc[:, 'Col_1'])
# Row_0    1
# Row_1    5
# Row_2    9
# Name: Col_1, dtype: int64

行の選択も可能。

print(df.loc['Row_1', :])
# Col_0    4
# Col_1    5
# Col_2    6
# Col_3    7
# Name: Row_1, dtype: int64

print(type(df.loc['Row_1', :]))
# <class 'pandas.core.series.Series'>

この場合、末尾の, :は省略できます。ndarrayのスライスと同様の考え方。

print(df.loc['Row_1'])
# Col_0    4
# Col_1    5
# Col_2    6
# Col_3    7
# Name: Row_1, dtype: int64

リストでの指定もできます。複数行・複数列を指定した場合はpandas.DataFrameとなります。

print(df.loc[['Row_0', 'Row_2'], ['Col_1', 'Col_3']])
#        Col_1  Col_3
# Row_0      1      3
# Row_2      9     11

print(type(df.loc[['Row_0', 'Row_2'], ['Col_1', 'Col_3']]))
# <class 'pandas.core.frame.DataFrame'>

単独の要素を選択したい場合はloc[]でもよいが、at[]のほうがより高速。

print(df.loc['Row_0', 'Col_1'])
# 1

print(type(df.loc['Row_0', 'Col_1']))
# <class 'numpy.int64'>

print(df.at['Row_0', 'Col_1'])
# 1

at[]は単独の要素の選択に特化しており、loc[]のようにスライスやリストでの範囲選択はできない。

# print(df.at[:, 'Col_1'])
# TypeError: 'slice(None, None, None)' is an invalid key

loc[], at[]は行名・列名で位置を指定するが、行番号・列番号で指定するiloc[], iat[]もあります。

print(df.iloc[[0, 2], [1, 3]])
#        Col_1  Col_3
# Row_0      1      3
# Row_2      9     11

print(df.iat[0, 1])
# 1

loc[], at[]や行・列の選択についての詳細は以下の記事を参照。

行名・列名や行番号・列番号ではなく、条件による抽出も可能。

print(df.query('Col_0 > 2'))
#        Col_0  Col_1  Col_2  Col_3
# Row_1      4      5      6      7
# Row_2      8      9     10     11

詳細は以下の記事を参照。

行・列・要素の変更 loc[]などで選択した範囲には新たな値を代入できます。

df.loc[:, 'Col_1'] = [10, 50, 90]

print(df)
#        Col_0  Col_1  Col_2  Col_3
# Row_0      0     10      2      3
# Row_1      4     50      6      7
# Row_2      8     90     10     11

全体を選択してまるごと変更することも可能。

df.loc[:] = np.arange(12).reshape(3, 4) * 100

print(df)
#        Col_0  Col_1  Col_2  Col_3
# Row_0      0    100    200    300
# Row_1    400    500    600    700
# Row_2    800    900   1000   1100

選択範囲と同じ形状でないとエラーになる。

# df.loc[:, 'Col_1'] = [10, 50, 90, 130]
# ValueError: Length of values does not match length of index

# df.loc[:] = np.arange(16).reshape(4, 4) * 100
# ValueError: cannot set using a slice indexer with a different length than the value

列ごとに様々な型を持つDataFrame

pandas.DataFrameは各列に異なる型のデータを格納できます。

df_multi = pd.read_csv('data/src/sample_pandas_normal.csv', index_col=0)

print(df_multi)
#          age state  point
# name                     
# Alice     24    NY     64
# Bob       42    CA     92
# Charlie   18    CA     70
# Dave      68    TX     70
# Ellen     24    CA     88
# Frank     30    NY     57

各列のデータ型はdtypes属性で確認できます。

print(df_multi.dtypes)
# age       int64
# state    object
# point     int64
# dtype: object

データ型は変更することも可能。以下の記事を参照。

数値だけでなく文字列や日時を含むデータを簡便に扱えるのはnumpy.ndarrayに対するpandas.DataFrameの大きな利点であります。

二次元配列・リストからDataFrameを作成

コンストラクタpandas.DataFrame()の第一引数dataに二次元のNumPy配列ndarrayを指定すると、その配列がそのままvaluesとなるpandas.DataFrameが生成される。デフォルトではcolumns, indexは連番の数値となります。

import pandas as pd
import numpy as np

print(np.arange(9).reshape(3, 3))
# [[0 1 2]
#  [3 4 5]
#  [6 7 8]]

print(pd.DataFrame(np.arange(9).reshape(3, 3)))
#    0  1  2
# 0  0  1  2
# 1  3  4  5
# 2  6  7  8

コンストラクタpandas.DataFrame()の引数columns, indexに任意の列名・行名を指定できます。

print(pd.DataFrame(np.arange(9).reshape(3, 3),
                   columns=['col_0', 'col_1', 'col_2'],
                   index=['row_0', 'row_1', 'row_2']))
#        col_0  col_1  col_2
# row_0      0      1      2
# row_1      3      4      5
# row_2      6      7      8

ndarrayではなく、二次元のリスト(リストのリスト)でも問題ありません。

print(pd.DataFrame([[0, 1, 2], [3, 4, 5], [6, 7, 8]]))
#    0  1  2
# 0  0  1  2
# 1  3  4  5
# 2  6  7  8

格納されたリストの要素数がバラバラの場合は足りない部分が欠損値NaNとなります。

print(pd.DataFrame([[0, 1, 2], [3, 4, 5], [6, 7, 8, 9, 10]]))
#    0  1  2    3     4
# 0  0  1  2  NaN   NaN
# 1  3  4  5  NaN   NaN
# 2  6  7  8  9.0  10.0

欠損値NaNの扱いについては以下の記事を参照。

複数の一次元配列・リストからDataFrameを作成

コンストラクタpandas.DataFrame()の第一引数dataには、キーを列名、値を各列の要素となるリストや配列、タプルなどとした辞書dictを指定できます。

print(pd.DataFrame({'col_0': [0, 1, 2],
                    'col_1': np.arange(3, 6),
                    'col_2': (6, 7, 8)}))
#    col_0  col_1  col_2
# 0      0      3      6
# 1      1      4      7
# 2      2      5      8

引数indexで行名を指定することももちろん可能。

print(pd.DataFrame({'col_0': [0, 1, 2],
                    'col_1': np.arange(3, 6),
                    'col_2': (6, 7, 8)},
                   index=['row_0', 'row_1', 'row_2']))
#        col_0  col_1  col_2
# row_0      0      3      6
# row_1      1      4      7
# row_2      2      5      8

辞書の値に指定するリストや配列などの要素数が一致していないとエラーになる。上述の二次元リスト(リストのリスト)のように足りない部分が欠損値NaNになることはない。

# print(pd.DataFrame({'col_0': [0, 1, 2, 100],
#                     'col_1': np.arange(3, 6),
#                     'col_2': (6, 7, 8)}))
# ValueError: arrays must all be same length

複数のリストや配列を列ではなく行にしたい場合は、.Tで転置する方法があります。

print(pd.DataFrame({'row_0': [0, 1, 2],
                    'row_1': np.arange(3, 6),
                    'row_2': (7, 8, 9)}).T)
#        0  1  2
# row_0  0  1  2
# row_1  3  4  5
# row_2  7  8  9

そのほか、pd.DataFrame.from_dict()で引数orient='index'とする方法もあります。この場合、辞書の値は同じ型でないとエラーになるので注意。

print(pd.DataFrame.from_dict(
    {'row_0': [0, 1, 2],
     'row_1': [3, 4, 5],
     'row_2': [6, 7, 8]},
    orient='index'))
#        0  1  2
# row_0  0  1  2
# row_1  3  4  5
# row_2  6  7  8

print(pd.DataFrame.from_dict(
    {'row_0': np.array([0, 1, 2]),
     'row_1': np.array([3, 4, 5]),
     'row_2': np.array([6, 7, 8])},
    orient='index'))
#        0  1  2
# row_0  0  1  2
# row_1  3  4  5
# row_2  6  7  8

# print(pd.DataFrame.from_dict(
#     {'row_0': [0, 1, 2],
#      'row_1': np.array([3, 4, 5]),
#      'row_2': [6, 7, 8]},
#     orient='index'))
# TypeError: Expected list, got numpy.ndarray

辞書のリスト・辞書からDataFrameを作成

コンストラクタpandas.DataFrame()の第一引数dataに辞書のリストを指定してもよい。 この場合、辞書のキーが列名となります。引数indexで行名を指定することも可能。

print(pd.DataFrame([{'col_0': 0, 'col_1': 1, 'col_2': 2},
                    {'col_0': 3, 'col_1': 4, 'col_2': 5},
                    {'col_0': 6, 'col_1': 7, 'col_2': 8}]))
#    col_0  col_1  col_2
# 0      0      1      2
# 1      3      4      5
# 2      6      7      8

print(pd.DataFrame([{'col_0': 0, 'col_1': 1, 'col_2': 2},
                    {'col_0': 3, 'col_1': 4, 'col_2': 5},
                    {'col_0': 6, 'col_1': 7, 'col_2': 8}],
                   index=['row_0', 'row_1', 'row_2']))
#        col_0  col_1  col_2
# row_0      0      1      2
# row_1      3      4      5
# row_2      6      7      8

それぞれの辞書のキーが一致していない場合は存在しない値が欠損値NaNで埋められる。

print(pd.DataFrame([{'col_0': 0, 'col_1': 1, 'col_2': 2},
                    {'col_0': 3, 'col_2': 5, 'col_3': 100},
                    {'col_0': 6, 'col_1': 7, 'col_2': 8}]))
#    col_0  col_1  col_2  col_3
# 0      0    1.0      2    NaN
# 1      3    NaN      5  100.0
# 2      6    7.0      8    NaN

辞書を値とする辞書でも問題ありません。外側の辞書のキーが列名、内側の辞書のキーが行名となります。

print(pd.DataFrame({'col_0': {'row_0': 0, 'row_1': 1, 'row_2': 2},
                    'col_1': {'row_0': 3, 'row_2': 4, 'row_3': 5},
                    'col_2': {'row_0': 6, 'row_1': 7, 'row_2': 8}}))
#        col_0  col_1  col_2
# row_0    0.0    3.0    6.0
# row_1    1.0    NaN    7.0
# row_2    2.0    4.0    8.0
# row_3    NaN    5.0    NaN

辞書を元にDataFrameを作成するユースケースとして実際にありえるのはWebAPIなどで取得したデータを処理する場合。そのような場合は、pandas.io.json.json_normalize()という関数を使うとネストした辞書などのより複雑な構造にも対応できます。以下の記事を参照。

またJSON形式の文字列やファイルを直接読み込むこともできます。

CSVファイルやExcelファイルから読み込み

実務上はコンストラクタでpandas.DataFrameを生成することはほとんどなく、ファイルから読み込むことが多い。 上述のJSONファイルのほか、CSVファイルやExcelファイル(xls, xlsx)をpandas.DataFrameとして読み込むことができます。 以下の記事を参照。

シェア

関連カテゴリー

Python pandas

pandasで特定の条件を満たす要素数をカウント(全体、行・列ごと) pandas.DataFrame, Seriesをpickleで保存、読み込み(to_pickle, read_pickle) pandasでcsv/tsvファイル読み込み(read_csv, read_table) pandasのバージョンを確認(pd.show_versions) pandas.DataFrameから条件を満たす行名・列名の行・列を抽出(選択) pandas.DataFrameに列や行を追加(assign, appendなど) 『Pythonデータサイエンスハンドブック』は良書(NumPy, pandasほか) pandas.DataFrame, Seriesの行をランダムソート(シャッフル) pandasでカテゴリ変数をダミー変数に変換(get_dummies) pandasのオプション設定を確認・変更する方法 pandas.DataFrameから特定の型dtypeの列を抽出(選択) pandas.DataFrameの行・列を指定して削除するdrop pandasで最大値・最小値の行名・列名を取得するidxmax, idxmin pandasで中央値を取得するmedian pandasでクリップボードの中身をDataFrameとして取得するread_clipboard

Last Updated: 6/26/2019, 10:34:03 PM