[pandas] query関数によるSeries, DataFrameの条件抽出

Series, DataFrameの統計量の求め方をまとめてみた。


# 公式のdocumentation
- [pandas.DataFrame.query](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.query.html)

# 目次
- 動作環境
- 今回扱うDataFrameについて
- 条件抽出: 単一条件
- 条件抽出: 複数条件
- 元のデータの変更
-

# 動作環境
|種類             |バージョン       |
|:---------------|:--------------|
|MacBook Air     |Ventura13.0.1  |
|python          |3.9.6          |
|jupyter notebook|6.5.2          |
|pandas          |1.5.2          |

まずはパッケージのインポートから
```python:
import pandas as pd
```
pandasを扱うときはpdが慣例だそう。

# 今回扱うDataFrameについて
[[python]pandas read_csvの備忘録](https://qiita.com/yossy_stu/items/a43cd624640f4de5daea)
と同じ手法だが、年平均黒点数ではなく一日当たりの黒点数のcsvデータを用いている。
[Sunspot Number|SILSO](https://www.sidc.be/silso/datafiles#)
```python:
df_dSN = pd.read_csv('SN_d_tot_V2.0.csv'
                    , sep=';'
                    , dtype={0:int, 1:int, 2:int, 3:float, 4:int, 5:float, 6:int, 7:int}
                    , names=('year', 'month', 'day', 'date_frac', 'num', 'std', 'obs spot', 'certanty'))
df_dSN.head(3)
#     year month  day  date_frac   num	 std  obs spot	certanty
# 0   1818	   1	1	1818.001	-1	-1.0	     0	       1
# 1	  1818	   1	2	1818.004	-1	-1.0	     0	       1
# 2	  1818	   1	3	1818.007	-1	-1.0	     0	       1
```
順に、
year:観測年、month:観測月、day:観測日、date_frac:小数点換算の年月日、num:黒点数、std:標準偏差、obs spot:観測地点数、certainty:確定or未確定

# DataFrame、Seriesの条件抽出の方法
今回はより表記がシンプルになるquery関数を紹介しているが、2通り存在する。
一つはもちのろんquery関数
```python:
df_dSN.query('date_frac >= 2000').head(3)
#       year	month	day	date_frac	num	std	obs spot	certanty
# 66474	2000	    1	  1	 2000.001	 71	3.8	      14	       1
# 66475	2000	    1	  2	 2000.004	 75	4.1	      10	       1
# 66476	2000	    1	  3	 2000.007	 80	3.9	      13	       1
```
もう一つは[]内で直に条件指定をしてやる手法
```python:
df_dSN[df_dSN['date_frac'] >= 2000].head(3)
#       year	month	day	date_frac	num	std	obs spot	certanty
# 66474	2000	    1	  1	 2000.001	 71	3.8	      14	       1
# 66475	2000	    1	  2	 2000.004	 75	4.1	      10	       1
# 66476	2000	    1	  3	 2000.007	 80	3.9	      13	       1
```
両方示すのは面倒なので前者のquery関数だけ以降示すが、上2つの表記からも明らかに前者の方がシンプルな記述ができる。

# 単一条件
```python:
df_dSN.query('date_frac >= 2000').head(3)
#       year	month	day	date_frac	num	std	obs spot	certanty
# 66474	2000	    1	  1	 2000.001	 71	3.8	      14	       1
# 66475	2000	    1	  2	 2000.004	 75	4.1	      10	       1
# 66476	2000	    1	  3	 2000.007	 80	3.9	      13	       1
```
''内で column名 比較演算子 数値のように条件を指定してやることでシンプルに条件を指定できる。

# 単一条件: 否定
単一でも複数でも条件の否定は2通りの表し方がある。
1つ目は!を使う手法。
```python:
df_dSN.query('certanty != 1').head(3)
#       year	month	day	date_frac	num	std	obs spot	certanty
# 74691	2022	    7	  1	 2022.497	 58	9.2	      40	       0
# 74692	2022	    7	  2	 2022.500	 64	5.8	      40	       0
# 74693	2022	    7	  3	 2022.503	 55	9.2	      32	       0
```
2つ目は条件の前にnotをつける方法。
```python:
df_dSN.query('not certanty == 1').head(3)
#       year	month	day	date_frac	num	std	obs spot	certanty
# 74691	2022	    7	  1	 2022.497	 58	9.2	      40	       0
# 74692	2022	    7	  2	 2022.500	 64	5.8	      40	       0
# 74693	2022	    7	  3	 2022.503	 55	9.2	      32	       0
```

# 複数条件
複数条件の場合は更にシンプルに記述ができる。例えばある期間を抜き出す際は、
```python:
df_dSN.query('2000 <= year <= 2010')
#       year	month	day	date_frac	num	std	obs spot	certanty
# 66474	2000	    1	  1	 2000.001	 71	3.8	      14	       1
# 66475	2000	    1	  2	 2000.004	 75	4.1	      10	       1
# 66476	2000	    1	  3	 2000.007	 80	3.9	      13	       1
# 66477	2000	    1 	  4	 2000.010	 95	7.5	      13	       1
# 66478	2000	    1	  5	 2000.012	108	5.1	      19	       1
# ...	...	...	...	...	...	...	...	...
# 70487	2010	   12	 27	 2010.988	 17	2.2	      13	       1
# 70488	2010	   12	 28	 2010.990	 22	1.9	      12	       1
# 70489	2010	   12	 29	 2010.993	 19	1.5	      15	       1
# 70490	2010	   12	 30	 2010.996	 14	2.8	       9	       1
# 70491	2010	   12	 31	 2010.999	 42	3.2	      10	       1
```
と一行で処理を完結できるかつ直感的に書ける。
また、複数要素の選択の場合はリストを''内に書けばいい。これも直感的。
```python:
df_dSN.query('year == [1990, 2000]')
#       year	month	day	date_frac	num	 std	obs spot	certanty
# 62822	1990	　　 1	  1	 1990.001	239	 8.6	      13	       1
# 62823	1990	　　 1	  2	 1990.004	226	12.8	      13	       1
# 62824	1990	　　 1	  3	 1990.007	213	13.0	      17	       1
# 62825	1990	　　 1	  4	 1990.010	223	10.5	      15	       1
# 62826	1990	　　 1	  5	 1990.012	210	 9.0	      17	       1
# ...	...	...	...	...	...	...	...	...
# 66835	2000	　  12	 27	 2000.988	162	 5.2      	   8	       1
# 66836	2000	　  12	 28	 2000.990	162	 7.0	      13	       1
# 66837	2000	　　12	 29	 2000.993	151	11.7	      15	       1
# 66838	2000	　　12	 30	 2000.996	152	10.6	      11	       1
# 66839	2000	　　12	 31	 2000.999	119	10.1	      11	       1
```

# 抽出した結果で元のDataFrameを更新する
抽出した条件で元のDataFrameを更新したい場合は()内でinplace=Trueと書く。
デフォルトはFalseとなっている。
```python:
df_dSN_2000_2010 = df_dSN
df_dSN_2000_2010.query('2000 <= year <= 2010', inplace=True)
df_dSN_2000_2010
#       year	month	day	date_frac	num	std	obs spot	certanty
# 66474	2000	    1	  1	 2000.001	 71	3.8	      14	       1
# 66475	2000	    1	  2	 2000.004	 75	4.1	      10	       1
# 66476	2000	    1	  3	 2000.007	 80	3.9	      13	       1
# 66477	2000	    1 	  4	 2000.010	 95	7.5	      13	       1
# 66478	2000	    1	  5	 2000.012	108	5.1	      19	       1
# ...	...	...	...	...	...	...	...	...
# 70487	2010	   12	 27	 2010.988	 17	2.2	      13	       1
# 70488	2010	   12	 28	 2010.990	 22	1.9	      12	       1
# 70489	2010	   12	 29	 2010.993	 19	1.5	      15	       1
# 70490	2010	   12	 30	 2010.996	 14	2.8	       9	       1
# 70491	2010	   12	 31	 2010.999	 42	3.2	      10	       1
```

# 参考にしたURL
- [pandas.DataFrameの行を条件で抽出するquery](https://note.nkmk.me/python-pandas-query/)
参考にしたサイトでは後者の方法も詳しく記載しているので困ったらご一読を(宣伝ではない)。