pandas.DataFrame, SeriesとNumPy配列ndarrayを相互に変換

Modified: | Tags: Python, pandas, NumPy

pandasのDataFrame, SeriesとNumPy配列ndarrayを相互に変換する方法を説明する。

DataFrame, Seriesndarrayに変換するにはto_numpy()メソッドかvalues属性、ndarrayDataFrame, Seriesに変換するにはそれぞれのコンストラクタを使う。

DataFrame, SeriesとPython組み込みのリスト型listの相互変換、DataFrameSeriesの相互変換については以下の記事を参照。

本記事のサンプルコードのpandasとNumPyのバージョンは以下の通り。バージョンによって仕様が異なる可能性があるので注意。

import pandas as pd
import numpy as np

print(pd.__version__)
# 2.1.4

print(np.__version__)
# 1.26.2

pandasのDataFrame, SeriesをNumPy配列ndarrayに変換

DataFrame, Seriesndarrayに変換するには、to_numpy()メソッドかvalues属性を使う。

to_numpy()メソッド

DataFrame, Seriesto_numpy()メソッドでNumPy配列ndarrayに変換できる。

df = pd.DataFrame({'A': [1, 2], 'B': [3, 4]}, index=['X', 'Y'])
print(df)
#    A  B
# X  1  3
# Y  2  4

print(df.to_numpy())
# [[1 3]
#  [2 4]]

print(type(df.to_numpy()))
# <class 'numpy.ndarray'>
s = df['A']
print(s)
# X    1
# Y    2
# Name: A, dtype: int64

print(s.to_numpy())
# [1 2]

print(type(s.to_numpy()))
# <class 'numpy.ndarray'>

行名indexや列名columnsは無視され、データ列のみが変換される。indexをデータとして扱いたい場合はreset_index()を使う。

以下、DataFrameを例とするが、引数などの基本的な使い方はSeriesの場合も同じ。

データ型の指定: 引数dtype

DataFrameの各列のデータ型dtypeが同じ場合、そのデータ型のndarrayが生成される。

df_int = pd.DataFrame({'A': [1, 2], 'B': [3, 4]}, index=['X', 'Y'])
print(df_int)
#    A  B
# X  1  3
# Y  2  4

print(df_int.dtypes)
# A    int64
# B    int64
# dtype: object

print(df_int.to_numpy())
# [[1 3]
#  [2 4]]

print(df_int.to_numpy().dtype)
# int64

DataFrameの各列のデータ型dtypeが異なる場合、型変換できる共通の型のndarrayが生成される。例えば整数intと浮動小数点数floatの列が混在するDataFrameは、float型のndarrayに変換される。

df_int_float = pd.DataFrame({'A': [1, 2], 'B': [0.1, 0.2]}, index=['X', 'Y'])
print(df_int_float)
#    A    B
# X  1  0.1
# Y  2  0.2

print(df_int_float.dtypes)
# A      int64
# B    float64
# dtype: object

print(df_int_float.to_numpy())
# [[1.  0.1]
#  [2.  0.2]]

print(df_int_float.to_numpy().dtype)
# float64

型変換できる共通の型が無い場合、例えば数値と文字列の列が混在するDataFrameは、object型のndarrayとなる。object型では要素がそれぞれの型を持つ。

df_int_str = pd.DataFrame({'A': [1, 2], 'B': ['abc', 'xyz']}, index=['X', 'Y'])
print(df_int_str)
#    A    B
# X  1  abc
# Y  2  xyz

print(df_int_str.dtypes)
# A     int64
# B    object
# dtype: object

print(df_int_str.to_numpy())
# [[1 'abc']
#  [2 'xyz']]

print(df_int_str.to_numpy().dtype)
# object

print(df_int_str.to_numpy()[0, 0])
# 1

print(type(df_int_str.to_numpy()[0, 0]))
# <class 'int'>

print(df_int_str.to_numpy()[0, 1])
# abc

print(type(df_int_str.to_numpy()[0, 1]))
# <class 'str'>

to_numpy()の引数dtypeにデータ型を指定可能。変換できない型を指定するとエラーになる。

print(df_int_float.to_numpy(dtype='float32'))
# [[1.  0.1]
#  [2.  0.2]]

print(df_int_float.to_numpy(dtype=int))
# [[1 0]
#  [2 0]]

# print(df_int_str.to_numpy(dtype=int))
# ValueError: invalid literal for int() with base 10: 'abc'

pandasおよびNumPyのデータ型についての詳細は以下の記事を参照。

DataFrameの特定のデータ型の列のみndarrayに変換したい場合はselect_dtypes()を使う。数値列のみを抽出することも可能。

df_int_float_str = pd.DataFrame({'A': [1, 2], 'B': [0.1, 0.2], 'C': ['abc', 'xyz']}, index=['X', 'Y'])
print(df_int_float_str)
#    A    B    C
# X  1  0.1  abc
# Y  2  0.2  xyz

print(df_int_float_str.select_dtypes('number').to_numpy())
# [[1.  0.1]
#  [2.  0.2]]

コピーを生成するか指定: 引数copy

デフォルトではto_numpy()は可能な限りビューを生成する。生成されたndarrayが元のDataFrameのビューの場合、二つのオブジェクトはメモリを共有し、一方を変更すると他方も変更される。

df = pd.DataFrame({'A': [1, 2], 'B': [3, 4]}, index=['X', 'Y'])
a = df.to_numpy()

print(np.shares_memory(df, a))
# True

a[0, 0] = 100
print(a)
# [[100   3]
#  [  2   4]]

print(df)
#      A  B
# X  100  3
# Y    2  4

to_numpy()の引数copyTrueとするとコピーが生成され、メモリを共有しない。

df = pd.DataFrame({'A': [1, 2], 'B': [3, 4]}, index=['X', 'Y'])
a_copy = df.to_numpy(copy=True)

print(np.shares_memory(df, a_copy))
# False

a_copy[0, 0] = 100
print(a_copy)
# [[100   3]
#  [  2   4]]

print(df)
#    A  B
# X  1  3
# Y  2  4

copy=Trueは必ずコピーを返すが、copy=False(デフォルト)は必ずビューを返すとは限らないので注意。例えば、DataFrameの各列のデータ型dtypeが異なる場合はビューが生成できずコピーが生成される。

df_int_float = pd.DataFrame({'A': [1, 2], 'B': [0.1, 0.2]}, index=['X', 'Y'])
a_float = df_int_float.to_numpy()

print(np.shares_memory(df_int_float, a_float))
# False

pandasおよびNumPyにおけるビューとコピーについての詳細は以下の記事を参照。

values属性

DataFrame, Seriesvalues属性でもNumPy配列ndarrayに変換できる。

公式ドキュメントではto_numpy()メソッドの使用が推奨されているが、pandas 2.1.4時点ではvalues属性を使っても警告などは出ない。

df = pd.DataFrame({'A': [1, 2], 'B': [3, 4]}, index=['X', 'Y'])
print(df)
#    A  B
# X  1  3
# Y  2  4

print(df.values)
# [[1 3]
#  [2 4]]

print(type(df.values))
# <class 'numpy.ndarray'>
s = df['A']
print(s)
# X    1
# Y    2
# Name: A, dtype: int64

print(s.values)
# [1 2]

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

values属性の動作はto_numpy()のデフォルトの動作と同じ。

DataFrameの各列のデータ型dtypeが異なる場合は型変換できる共通の型のndarrayが生成され、型変換できる共通の型が無い場合はobject型のndarrayが生成される。

df_int_float = pd.DataFrame({'A': [1, 2], 'B': [0.1, 0.2]}, index=['X', 'Y'])
print(df_int_float.values)
# [[1.  0.1]
#  [2.  0.2]]

print(df_int_float.values.dtype)
# float64

df_int_str = pd.DataFrame({'A': [1, 2], 'B': ['abc', 'xyz']}, index=['X', 'Y'])
print(df_int_str.values)
# [[1 'abc']
#  [2 'xyz']]

print(df_int_str.values.dtype)
# object

可能な限りビューを生成するが、ビューを生成できない場合はコピーが生成される。

df = pd.DataFrame({'A': [1, 2], 'B': [3, 4]}, index=['X', 'Y'])
print(np.shares_memory(df, df.values))
# True

df_int_float = pd.DataFrame({'A': [1, 2], 'B': [0.1, 0.2]}, index=['X', 'Y'])
print(np.shares_memory(df_int_float, df_int_float.values))
# False

必ずコピーを生成するにはcopy()を使う。

print(np.shares_memory(df, df.values.copy()))
# False

NumPy配列ndarrayをpandasのDataFrame, Seriesに変換

ndarrayDataFrame, Seriesに変換するにはそれぞれのコンストラクタを使う。コンストラクタの第一引数datandarrayを指定できる。

pandas.DataFrame()

コンストラクタpd.DataFrame()の第一引数datandarrayを指定してDataFrameを生成できる。

a_2d = np.array([[1, 2], [3, 4]])
print(a_2d)
# [[1 2]
#  [3 4]]

print(pd.DataFrame(a_2d))
#    0  1
# 0  1  2
# 1  3  4

一次元配列の場合は一列のDataFrameが生成されるが、三次元以上の場合はエラーになる。

a_1d = np.array([1, 2])
print(a_1d)
# [1 2]

print(pd.DataFrame(a_1d))
#    0
# 0  1
# 1  2

a_3d = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])
print(a_3d)
# [[[1 2]
#   [3 4]]
# 
#  [[5 6]
#   [7 8]]]

# print(pd.DataFrame(a_3d))
# ValueError: Must pass 2-d input. shape=(2, 2, 2)

行名indexや列名columns、データ型dtypeなどを引数で指定可能。

print(pd.DataFrame(data=a_2d, index=['X', 'Y'], columns=['A', 'B'], dtype=float))
#      A    B
# X  1.0  2.0
# Y  3.0  4.0

コンストラクタpd.DataFrame()の詳細は以下の記事を参照。

ビューとコピー(メモリの共有)

第一引数datandarrayを指定する場合、デフォルトではpd.DataFrame()は可能な限りビューを生成する。生成されたDataFrameが元のndarrayのビューの場合、二つのオブジェクトはメモリを共有し、一方を変更すると他方も変更される。

a = np.array([[1, 2], [3, 4]])
df = pd.DataFrame(a)

print(np.shares_memory(a, df))
# True

a[0, 0] = 100
print(a)
# [[100   2]
#  [  3   4]]

print(df)
#      0  1
# 0  100  2
# 1    3  4

pd.DataFrame()の引数copyTrueとするとコピーを生成する。

a = np.array([[1, 2], [3, 4]])
df_copy = pd.DataFrame(a, copy=True)

print(np.shares_memory(a, df_copy))
# False

a[0, 0] = 100
print(a)
# [[100   2]
#  [  3   4]]

print(df_copy)
#    0  1
# 0  1  2
# 1  3  4

copy=Trueは必ずコピーを返すが、copy=Falsendarrayを指定したときのデフォルト)は必ずビューを返すとは限らないので注意。例えば、引数dtypeを指定して型が変換される場合はビューが生成できずコピーが生成される。

a = np.array([[1, 2], [3, 4]])
df_float = pd.DataFrame(a, dtype=float)

print(np.shares_memory(a, df_float))
# False

pandas.Series()

コンストラクタpd.Series()も基本的な使い方はpd.DataFrame()と同じ。

第一引数dataに一次元のndarrayを指定してSeriesを生成できる。

a_1d = np.array([1, 2])
print(a_1d)
# [1 2]

print(pd.Series(a_1d))
# 0    1
# 1    2
# dtype: int64

二次元以上の場合はエラー。

a_2d = np.array([[1, 2], [3, 4]])
print(a_2d)
# [[1 2]
#  [3 4]]

# print(pd.Series(a_2d))
# ValueError: Data must be 1-dimensional, got ndarray of shape (2, 2) instead

ラベルindexや名前name、データ型dtypeなどを引数で指定可能。

print(pd.Series(a_1d, index=['A', 'B'], name='my_series', dtype=float))
# A    1.0
# B    2.0
# Name: my_series, dtype: float64

ビューとコピー(メモリの共有)についてもpd.DataFrame()と同様。

デフォルトではpd.Series()は可能な限りビューを生成し、引数copyTrueとするとコピーを生成する。copy=False(デフォルト)は必ずビューを返すとは限らず、例えば引数dtypeを指定して型が変換される場合はビューが生成できずコピーが生成される。

a = np.array([1, 2])
print(np.shares_memory(a, pd.Series(a)))
# True

print(np.shares_memory(a, pd.Series(a, copy=True)))
# False

print(np.shares_memory(a, pd.Series(a, dtype=float)))
# False

関連カテゴリー

関連記事