では今回はpandasの最も基本的なオブジェクトとなるSeriesについて扱っていきます。
では早速見ていきましょう。

Seriesオブジェクトとは

公式ドキュメントによると、Seriesオブジェクトは以下のように記述されています。

One-dimensional ndarray with axis labels  
(including time series)

簡単に言えば一次元配列のことを指します。
NumPyにおける配列と同じように見えますが、扱っていく上ではいくつかの点で異なります。

  • インデックスを番号以外で振ることができる。
  • オブジェクトそのものに名前をつけることができる。
  • 時間データを格納できる(pandasのTime Seriesが扱える)

これらはpandasNumPyの違いにも当てはまります。

Seriesには1セットのデータが格納されており、これらをつなぎあわせるとDataFrameオブジェクトになります。DataFrameオブジェクトについてはまた別のページで取り上げる予定です。

SeriesオブジェクトのAPIドキュメントは以下のとおりです。

class pandas.Series(data=None, index=None, dtype=None, name=None, copy=False, fastpath=False)

params:

パラメータ名 概要
data (省略可能)初期値None
配列, 辞書,
または数値
Seriesに格納するデータを指定します
index (省略可能)初期値None
配列に相当するもの
もしくは1次元の
Indexオブジェクト
データの値それぞれに対応するインデックスを指定します。インデックスの値が被っていても問題ありません。インデックスが指定されない場合、(0, 1, 2, …,)と順番につけられます。
dtype numpy.dtype
またはNone
(省略可能)初期値None
データ型を指定します。Noneの場合はdataで入力された値から推測します。
copy bool値 (省略可能)初期値False
入力されたデータを参照しながら使う(False)のか、コピーを作成して用いる(True)のかを指定します。
name 文字列
または数値
データセット自体のラベルを指定します。

引数の名前から大体予想がつくものばかりだと思います。
引数fastpathについてですが、ほとんど使用されることがなく公式ドキュメントでも言及がないため割愛します。

では、これを踏まえてSeriesオブジェクトを実際に使ってみましょう。

実際に使ってみる

Seriesオブジェクトの生成

Seriesオブジェクトの格納できるデータは配列に相当するオブジェクト、辞書(dict)、または単体の数値となっています。
まずは簡単な配列から生成してみます。

In [1]: import pandas as pd # pandasモジュールのインポート

In [2]: a = pd.Series([1,2,3]) # 配列から生成

In [3]: a
Out[3]:
0    1
1    2
2    3
dtype: int64

In [4]: array = [1., 2., 3.] # もちろんfloat型もOK.

In [5]: b = pd.Series(array)

In [6]: b
Out[6]:
0    1.0
1    2.0
2    3.0
dtype: float64

NumPyの1次元配列から生成することもできます。

In [7]: import numpy as np # numpyモジュールのインポート

In [8]: np_array = np.array([1,2,3])

In [9]: c = pd.Series(np_array)

In [10]: c
Out[10]:
0    1
1    2
2    3
dtype: int64

辞書から作成します。

In [15]: dic = {"Tokyo": 100, "Osaka": 250, "Nagoya": 10} # 辞書オブジェクトの生成

In [16]: d = pd.Series(dic)

In [17]: d # インデックスが辞書のキーになっている
Out[17]:
Nagoya     10
Osaka     250
Tokyo     100
dtype: int64

値だけを入れることもできます。

In [20]: e = pd.Series(1) # 1だけを入れる

In [21]: e
Out[21]:
0    1
dtype: int64

文字列も格納できます。

In [22]: f = pd.Series(["A", "B", "C"])

In [23]: f
Out[23]:
0    A
1    B
2    C
dtype: object

これらをミックスして入れることも可能です。

In [26]: g = pd.Series(['A', 1, 1.0, None])

In [27]: g
Out[27]:
0       A
1       1
2       1
3    None
dtype: object

Noneは欠損値を表します。

インデックスの指定

次にインデックスを指定していきます。
デフォルトでは入力された値から順に0,1,2,…とつけられていきます。
このときRangeIndexクラスによって自動的につけられています。

In [29]: series = pd.Series([5,4,3,2,1])

In [30]: series
Out[30]:
0    5
1    4
2    3
3    2
4    1
dtype: int64

In [31]: series.index # indexの表示
Out[31]: RangeIndex(start=0, stop=5, step=1)

始点startが0で終点stopが5、間隔step1でとった数列がインデックスとなっているという意味になっています。
stopはインデックスには含まれてないことに注意です。
このオブジェクトはただ順番にインデックスをつけるためのものなのでNumPyのnp.arange()関数でも代用できます。

In [41]: series_2 = pd.Series([5,4,3,2,1], index=np.arange(5))

In [42]: series_2
Out[42]:
0    5
1    4
2    3
3    2
4    1
dtype: int64

In [43]: series_2.index
Out[43]: Int64Index([0, 1, 2, 3, 4], dtype='int64')

Int64Indexオブジェクトというのはpandasに実装されているIndexオブジェクトの中でIndexが整数のみで構成されているもののことを指します。 基本的にはIndexオブジェクトに格納されますが特殊なケースにおいてはこのようなものが使われます。
Indexに関するオブジェクトの種類についてはpandas側が勝手に判断してやってくれるのでindexによって種類が変わるんだなあ程度にしておいて問題はないでしょう。

では次に文字列でインデックスをつけてみます。

In [53]: series_3 = pd.Series([5,4,3,2,1], index=['a','b','c','d','e']) # a,b,c,d,eとインデックスをつける

In [54]: series_3
Out[54]:
a    5
b    4
c    3
d    2
e    1
dtype: int64

In [55]: series_3.index
Out[55]: Index(['a', 'b', 'c', 'd', 'e'], dtype='object')

うまくできました。
先程も触れた通り、辞書でデータを指定すればインデックスも同時に指定することになります。

In [56]: dic_2 = {'a':5, 'b':4, 'c':3, 'd':2, 'e':1}

In [57]: series_4 = pd.Series(dic_2)

In [58]: series_4
Out[58]:
a    5
b    4
c    3
d    2
e    1
dtype: int64

In [59]: series_4.index
Out[59]: Index(['a', 'b', 'c', 'd', 'e'], dtype='object')

生成されているSeriesオブジェクトに対してIndexをつけ直すことも可能です。

In [61]: series_2
Out[61]:
0    5
1    4
2    3
3    2
4    1
dtype: int64


In [62]: series_2.index = ['a', 'b', 'c', 'd','e']

In [63]: series_2
Out[63]:
a    5
b    4
c    3
d    2
e    1
dtype: int64

また、Seriesオブジェクトを生成する際に辞書を使い、その上でIndex指定をすると辞書のキーとして使われていないインデックスの値はNaNとなります。

In [65]: dic_2
Out[65]: {'a': 5, 'b': 4, 'c': 3, 'd': 2, 'e': 1}

In [66]: series_5 = pd.Series(dic_2, index=['a','c','e','f'])

In [67]: series_5 # 指定されていないindexの値は表示されない。
Out[67]:
a    5.0
c    3.0
e    1.0
f    NaN
dtype: float64

In [68]: series_5.index = ['a', 'b', 'c', 'd', 'e'] # あとから指定するときは要素数と同じ数だけ指定しないとエラーが返ってくる。

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-68-6c858c0842cf> in <module>()
----> 1 series_5.index = ['a', 'b', 'c', 'd', 'e']
(エラーメッセージが表示される)

ValueError: Length mismatch: Expected axis has 4 elements, new values have 5 elements

引数copyの操作

引数copyTrueにすると、dataとしてSeriesが指定されたとき、コピーが作成され、参照元が引き渡されなくなります。

In [89]: array_2 = [20,0,0,0,0]

In [90]: series_6 = pd.Series(array_2)

In [100]: series_6 # もととなるSeriesを生成
Out[100]:
0    20
1     0
2     0
3     0
4     0
dtype: int64

In [101]: series_7 = pd.Series(series_6, copy=False) # こちらはFalse

In [102]: series_8 = pd.Series(series_6, copy=True) # こちらはTrue

In [103]: series_7
Out[103]:
0    20
1     0
2     0
3     0
4     0
dtype: int64

In [104]: series_7[0] = 11 # Falseにした方の値を変更する

In [105]: series_7
Out[105]:
0    11
1     0
2     0
3     0
4     0
dtype: int64

In [106]: series_6 # もとのSeriesにも変更が反映されている
Out[106]:
0    11
1     0
2     0
3     0
4     0
dtype: int64

In [107]: series_8 # Trueにしておくとそのような変更の影響を受けない
Out[107]:
0    20
1     0
2     0
3     0
4     0
dtype: int64

In [108]: series_6[0] = 10

In [109]: series_7
Out[109]:
0    10
1     0
2     0
3     0
4     0
dtype: int64

In [110]: series_8
Out[110]:
0    20
1     0
2     0
3     0
4     0
dtype: int64

copy関数でもコピーを生成することができます。

In [111]: series_9 = series_6.copy()

要素の抜き出し

要素を抜き出してみましょう。
まずはインデックスの指定から。

In [70]: series
Out[70]:
0    5
1    4
2    3
3    2
4    1
dtype: int64

In [71]: series[0]
Out[71]: 5

In [72]: series[0:2] # スライス表記もできる。
Out[72]:
0    5
1    4
dtype: int64

偶数だけ。

In [73]: series[series%2 == 0]
Out[73]:
1    4
3    2
dtype: int64

インデックスが文字列でも抜き出すことが可能です。

In [78]: series_3
Out[78]:
a    5
b    4
c    3
d    2
e    1
dtype: int64

In [79]: series_3['a']
Out[79]: 5

In [80]: series_3[['a','c']] # 複数抜き出す場合はリストにする必要あり
Out[80]:
a    5
c    3
dtype: int64

簡単な操作

SeriesオブジェクトではNumPy配列のような操作をすることが可能です。

In [81]: series
Out[81]:
0    5
1    4
2    3
3    2
4    1
dtype: int64

In [82]: series + 1
Out[82]:
0    6
1    5
2    4
3    3
4    2
dtype: int64

In [83]: series + pd.Series([1,1,2,2,2])
Out[83]:
0    6
1    5
2    5
3    4
4    3
dtype: int64

In [84]: series * 3
Out[84]:
0    15
1    12
2     9
3     6
4     3
dtype: int64

合計などを求めることも可能です。

In [87]: series.sum()
Out[87]: 15

In [88]: series.std()
Out[88]: 1.5811388300841898

NumPy関数の適用

SeriesはNumPyとの親和性が高いため、NumPyの関数を使った数値処理をすることが簡単にできます。

In [85]: np.sum(series) # 合計を求める
Out[85]: 15

In [86]: np.log(series)
Out[86]:
0    1.609438
1    1.386294
2    1.098612
3    0.693147
4    0.000000
dtype: float64

値の追加、変更の仕方

値の追加や変更はインデックスを指定することで可能です。

In [147]: series = pd.Series([0,0,0,0,0])

In [148]: series
Out[148]:
0    0
1    0
2    0
3    0
4    0
dtype: int64

In [149]: series[7] = 10

In [150]: series
Out[150]:
0     0
1     0
2     0
3     0
4     0
7    10
dtype: int64

In [151]: series['a'] = 11

In [152]: series
Out[152]:
0     0
1     0
2     0
3     0
4     0
7    10
a    11
dtype: int64

In [153]: series['a'] = 12 # 上書きもできる

In [154]: series
Out[154]:
0     0
1     0
2     0
3     0
4     0
7    10
a    12
dtype: int64

時系列データの扱い

pandasに実装されているTimeSeriesをSeriesの中に組み込むことができます。
日付のデータを作りSeriesオブジェクトに格納してみましょう。

In [177]: data = pd.date_range('2018/05/26', periods=10,freq='D') # 時系列データの作成

In [178]: data
Out[178]:
DatetimeIndex(['2018-05-26', '2018-05-27', '2018-05-28', '2018-05-29',
               '2018-05-30', '2018-05-31', '2018-06-01', '2018-06-02',
               '2018-06-03', '2018-06-04'],
              dtype='datetime64[ns]', freq='D')

In [179]: date_series = pd.Series(data)

In [180]: date_series
Out[180]:
0   2018-05-26
1   2018-05-27
2   2018-05-28
3   2018-05-29
4   2018-05-30
5   2018-05-31
6   2018-06-01
7   2018-06-02
8   2018-06-03
9   2018-06-04
dtype: datetime64[ns]

In [181]: date_series_2 = pd.Series(np.random.randn(10),index=data) # インデックスとして指定することも可能

In [182]: date_series_2
Out[182]:
2018-05-26   -0.335079
2018-05-27    0.099053
2018-05-28   -0.155142
2018-05-29    0.448569
2018-05-30   -0.839239
2018-05-31    0.768965
2018-06-01    0.320166
2018-06-02   -1.122765
2018-06-03    0.331456
2018-06-04   -1.453074
Freq: D, dtype: float64

TimeSeriesはpandasの特徴的な機能の1つなのでまた別の記事で詳しく取り上げます。

Seriesの属性(Attributes)

Seriesには様々な属性が含まれています。
DataFrameと混同して使っても問題ないように追加されている属性もあります。
公式ドキュメントにリストがあったので著者なりに噛み砕いた形で掲載しておきます。
iloclocといった重要なものについてはまた別のページで取り上げていきます。

属性(Attribute) 説明
T 軸を入れ替えたものを返します。Seriesオブジェクトそのものを返します。
asobject Series自体をオブジェクト化したものを返します。dataもリストの形で内包されています。
at at[インデックス]の形で使用。値を抜き出します。
axes インデックスとなっているものを返します。
base このSeriesオブジェクトが参照しているオブジェクトがあった場合、そのオブジェクトを返します。
blocks (非推奨) 内部プロパティを表示します。as_blocks()で同様の表示ができる。
data data部分のポインター(メモリー内の住所)を表示します。
dtype data部分に使われているデータ型を表示します。
dtypes 同上
flags 説明無し。オブジェクト自身の情報を表示します。
ftype data部分がparsedenseのときそれを表示します。
ftypes 同上
hasnans NaN値があるかどうかを返します。これでいろんな操作の速度を上げることが可能です。(おそらく他のNaN値を調べる関数より高速に調べられるからでしょう)
iat インデックスの値に関係なく、iat[i]でi番目の値にアクセスできます。
iloc インデックスの値ではなく番号指定で目的の値にアクセスできます。複数の値の指定も可能。
index インデックスに使われているオブジェクトを返します。
is_monotonic dataの値が単調増加かどうかをTrueかFalseで返します。
is_monotonic_decreasing 単調減少かどうかを返します。
is_monotonic_decreasing 単調増加かどうかを返します。
is_unique (保留)
itemsize dataの1つの値が使用しているメモリ量をバイト(byte)単位で返します。
ix (非推奨) アイテムの位置を指定することによってその値を表示します。まぎらわしさがあったのでilocとlocを使うことが推奨されています。
loc インデックスの値を指定することで該当する値を指定します。loc[インデックス]の形で使います。
nbytes データ部分が使用しているメモリ量を返します。単位はバイト(byte)です。
ndim 次元数を返します。Seriesの場合は1です。
shape data部分の形状(shape)を返します。Seriesの場合は1次元配列と同様”(アイテムの個数,)”の形で返されます。
size data部分のアイテム数を返します。
strides メモリ上で何バイト分移動すれば次のアイテムを読み込むことができるかを返します。float64なら8バイトです。
values データ部分のみを返します。
empty data部分に何も指定されていなければTrueが帰ってきます。
name name引数で指定した値を返します。
real valuesと同様。data部分を返します。

まとめ

今回はSeriesオブジェクトの使い方を中心的にまとめました。
ここで扱った操作はSeriesオブジェクトの集合体であるDataFrameオブジェクトと共通点が多いです。データとして扱う上でDataFrameとSeriesの違いをあまり意識することなくコーディングができるようpandasは作られているということなのでしょう。

参考