（Python）Excelファイルを読み込んで指定データのみを絞り込む方法

## Excelファイル内の指定社員コードのデータを検索して確認したい。
Excelファイル内の指定社員コードのデータをpythonで読み込んで、検索したいと思います。

**sample.xlsx**
![excelDemo_2_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/342307/3b680d49-a209-5ed5-7b2c-07239dd80ea4.png)


## ０. pandas のインストール
今回は pandas を使用します。（pandas の依存ライブラリは下記の通りです。）

```
openpyxl==2.6.3
pandas==0.25.1
xlrd==1.2.0
```

下記のコマンドでインストールします。

```
pip install pandas openpyxl xlrd
```



## １．ExcelFileの読み込み（pandas.read_excel または pandas.ExcelFile の parse メソッド）

Excelファイルの読み込み処理には **pandas.read_excel** または、**pandas.ExcelFile** インスタンスの **parse** メソッドを使用して、**pandas.DataFrame に変換します。** （今回は pandas.ExcelFile インスタンスの parse メソッドを使用しています。）

```readExcelModule.py
from pandas import DataFrame, Series, ExcelFile
import os

def isExcelFilePath(filepath:str)->bool:
    return (filepath.endswith('.xlsx')  
            or filepath.endswith('.xls'))

def getExcelFile(filepath:str)->ExcelFile:
    if (not os.path.exists(filepath)
        or (not isExcelFilePath(filepath))):
        return None
    return ExcelFile(filepath)
    
def getDataFromExcelFile(excelFile:ExcelFile)->DataFrame:
    return excelFile.parse(
        index_col=None
    )

dataFrame = getDataFromExcelFile(
    getExcelFile('./sample.xlsx')
)
print (dataFrame)
```

このとき**index_col=None** を指定しないと、一列目がindex として扱われてしまうので、index_col=None を指定するようにします。

```出力結果.txt
  社員コード   社員名    社員名かな 性別       生年月日
0  A000  田中太郎   たなかたろう  男 1980-01-01
1  C123  多田久信  おおたひさのぶ  男 1980-01-01
2  C012  安村花子  やすむらはなこ  女 1980-01-01
```



## ２. dataFrame のアクセサ[]に検索条件の絞り込み処理を渡す


```readExcelModule.py
searchedDataRow = dataFrame[dataFrame['社員コード'] == 'A000']
```
**dataFrame[dataFrame[検索対象列] == 検索条件の値]**と書くことで、DataFrame内の絞り込みができ**社員コードが「A000」の社員データ**を取得できます。

```出力結果.txt
  社員コード   社員名   社員名かな 性別       生年月日
0  A000  田中太郎  たなかたろう  男 1980-01-01

```

## （補足）「dataFrame[検索対象列] == 検索条件の値」は何を返すのか？
dataFrameのアクセサに渡した**「dataFrame[検索対象列] == 検索条件の値」**という記述は、**DataFrameの各行が検索条件にマッチするかのbool値リストを返す**ようです。

```readExcelModule.py
print (dataFrame['社員コード'] == 'A000')
```

```出力結果.txt
0     True
1    False
2    False
Name: 社員コード, dtype: bool
```
よって、全行の検索条件にマッチしたものだけがTrueとして渡されるため、結果として検索結果が取得できるようでした。

なので下記のような比較演算子を使用した検索も可能なようです。

```readExcelModule.py
# dataFrameの「固定支給額」が 380000 超のデータを検索する
searchedDataRow = dataFrame[dataFrame['固定支給額'] > 380000]
```

## （補足２）Header行なしの データのみを読み込みたい場合
Header行を読み込みデータに含めたくない場合、ExcelFile の parse（または pandas.read_excel）で下記の設定をします。

- skiprows を設定する
- header=None を設定する

これで、1行目をスキップして、header をただの連番として読み込めました。

```readExcelModule.py
def getDataFromExcelFile(excelFile:ExcelFile)->DataFrame:
    return excelFile.parse(
        index_col=None,
        skiprows=1,
        header=None
    )

```

```出力結果.txt
      0     1        2  3          4
0  A000  田中太郎   たなかたろう  男 1980-01-01
1  C123  多田久信  おおたひさのぶ  男 1980-01-01
2  C012  安村花子  やすむらはなこ  女 1980-01-01
```

## まとめ

- Excel を pandas.DataFrameとして読み込む。
- DataFrameの絞り込みを行うときは、DataFrame自体の絞り込み結果のbool値のリストを渡す。
- header=None と skiprows を設定するとヘッダの読み込みを制御できる。

以上を行うことで PythonでExcelファイルを読み込んで指定したデータを確認できました。

## 20210201 追記 絞り込みの処理で「FutureWarning: elementwise comparison failed; returning scalar instead, but in the future will perform elementwise comparison res_values = method(rvalues) 」と表示される場合

エラーメッセージを確認したところバージョンアップでpythonとpandasの挙動が、自分が記事を書いたときから変わったところがあるようです。

以下のバージョンで再検証しました。

``` 
py --version
Python 3.7.9
```

```
et-xmlfile-1.0.1 
jdcal-1.4.1 
numpy-1.20.0 
openpyxl-3.0.6 
pandas-1.2.1 
python-dateutil-2.8.1 
pytz-2021.1 
six-1.15.0 
xlrd-2.0.1
```

再検証したところ、dataFrameに対する比較演算処理（dataFrame['商品番号'] == 1)）が以前はboolのSeries値が返っていましたが、ただのbool値を返すように変わったようです。
https://stackoverflow.com/questions/40659212/futurewarning-elementwise-comparison-failed-returning-scalar-but-in-the-futur

以下のコードで動きました。

```py
dataFrame = getDataFromExcelFile(
    getExcelFile('./商品リスト.xlsx')
)
print (dataFrame)
searchedDataRow = dataFrame[dataFrame['商品番号'].isin([1])]
print (searchedDataRow)
```

```出力結果
 py .\demo.py
   商品番号 商品名   価格
0     1   a  100
1     2   b  200
2     3   c  300
3     4   d  400
   商品番号 商品名   価格
0     1   a  100
```

（教えて頂きありがとうございました。
記事も更新しました。）


以上です。