pandas.Seriesで、値の型が揃っていないときの動き

# 環境
* pandas v1.5.1
* Python v3.10.2

# 概要
`pandas.Sereis`で、値の型が揃っていないときの動きにハマりました。
後学のため、値の型が揃っていないときの動きをまとめます。

# 値の型が揃っていないときの挙動
## `dtype`が`string`の`pandas.Series`に、文字列以外を代入
`dtype`が`string`の`pandas.Series`に対して数値またはbool値を代入しようとすると、`ValueError`が発生します。

```
In [167]: s1 = pandas.Series(["a", "b"], dtype="string")

In [168]: s1
Out[168]: 
0    a
1    b
dtype: string

In [169]: s1[0] = 1
---------------------------------------------------------------------------
ValueError: Cannot set non-string value '1' into a StringArray.

In [170]: s1[0] = True
---------------------------------------------------------------------------
ValueError: Cannot set non-string value 'True' into a StringArray.
```

## 数値と文字列のlistから`pandas.Series(dtype="string")`を生成
数値と文字列のlistから`pandas.Series`を生成する際、`dtype="string"`を指定すると、数値は文字列に変換されます。

```
In [198]: s2 = pandas.Series([1, "b"], dtype="string")

In [199]: s2
Out[199]: 
0    1
1    b
dtype: string

In [200]: type(s2[0])
Out[200]: str
```

## `dtype`が`int64`の`pandas.Series`に、文字列を代入
`dtype`が`int64`の`pandas.Series`に対して文字列を代入すると、`dtype`は`object`に変わります。エラーは発生しません。

```
In [202]: s3 = pandas.Series([1, 2])

In [203]: s3
Out[203]: 
0    1
1    2
dtype: int64

In [204]: s3[0] = "a"

In [205]: s3
Out[205]: 
0    a
1    2
dtype: object
```

## `dtype`が`Int64`の`pandas.Series`に、文字列を代入
`dtype`が`Int64`の`pandas.Series`に対して文字列、bool値、小数を代入すると、`TypeError`が発生します。
小数を代入してもエラーになるところが、nullableな型の特徴です。

```
In [211]: s4 = pandas.Series([1, 2], dtype="Int64")

In [212]: s4
Out[212]: 
0    1
1    2
dtype: Int64

In [213]: s4[0] = "a"
---------------------------------------------------------------------------
TypeError: Invalid value 'a' for dtype Int64

In [214]: s4[0] = True
---------------------------------------------------------------------------
TypeError: Invalid value 'True' for dtype Int64

In [279]: s4[0] = 1.2
---------------------------------------------------------------------------
TypeError: Invalid value '1.2' for dtype Int64

```


## 数値と文字列のlistから`pandas.Series(dtype="int64")`を生成
数値と文字列のlistから`pandas.Series`を生成する際、dtype="int64"を指定すると、数値に変換できるならエラーは発生しません。ただし警告メッセージが表示されます。

```
In [260]: s5 = pandas.Series([1, "b"], dtype="int64")
---------------------------------------------------------------------------
ValueError: invalid literal for int() with base 10: 'b'

n [272]: s5 = pandas.Series([1, "2"], dtype="int64")
/home/vagrant/.pyenv/versions/3.10.2/lib/python3.10/site-packages/numpy/core/numeric.py:2463: FutureWarning: elementwise comparison failed; returning scalar instead, but in the future will perform elementwise comparison
  return bool(asarray(a1 == a2).all())
<ipython-input-272-ddb554c61a9c>:1: FutureWarning: Values are too large to be losslessly cast to int64. In a future version this will raise OverflowError. To retain the old behavior, use pd.Series(values).astype(int64)
  s5 = pandas.Series([1, "2"], dtype="int64")

 [274]: s5
Out[274]: 
0    1
1    2
dtype: int64
```

# まとめ
各挙動を確認した結果、以下であることが分かりました。

* `Int64`, `string`など[nullableな型](https://pandas.pydata.org/docs/user_guide/missing_data.html#experimental-na-scalar-to-denote-missing-values)に対して異なる値を代入しようとしたら、エラーが発生しました。ただし発生するエラーは`TypeError`と`ValueError`で、それぞれ異なりました。
* `int64`などnullableでない型に対して、異なる値を代入すると、より広い範囲を表す型に変わります。
* `pandas.Series`生成時には、`dtype`引数で指定した型に変換を試みます。変換できない場合はエラーが発生します。



# 補足

## `pandas.Series`生成時に`dtype="string"`を指定しない場合
`pandas.Series`に文字列のlistを渡した場合、`dtype`を指定しなければ`dtype`は`object`になります。
`object`は最も汎用的な型なので、この場合は異なる値を代入してもエラーは発生しません。

```
[206]: s5 = pandas.Series(["a", "b"])

In [207]: s5
Out[207]: 
0    a
1    b
dtype: object

In [208]: s5[0] = 1

In [209]: s5
Out[209]: 
0    1
1    b
dtype: object

In [210]: type(s5[0])
Out[210]: int
```