Pythonでのチームアプリ開発に参画するための学習履歴~基礎項目の補足とjupyterLab環境構築~

## 前回の記事

[Pythonでのチームアプリ開発に参画するための学習履歴~paizaラーニングの「Python3入門」を終えて~](https://qiita.com/shitikakei/items/832b3ad636be0f662586)

## 前回の記事の補足

#### スライスという概念

```python:
word = 'hello'
print(word[0:2]) #0番目の要素から2番めの要素まで取り出す

## 処理結果
he

```
いわゆるインデックスで要素の位置を指定したりすることをPythonではスライスというようだ。

もちろんリストにもスライス表記はある

```Python:

wordlist = ['apple','bike','cake','drum','electric']
print(wordlist[2:]) # 2番目の要素から最後まで取り出す
print(wordlist[:2]) # 最初から2番めの要素まで取り出す

## 処理結果

['cake', 'drum', 'electric']
['apple', 'bike']

```

## リストについて


リストの最後に要素を追加する
`.append()`

リストから引数に指定した要素を削除する
`.remove()`

リストの要素をすべて削除する
`.clear()`

リストのi番目にxを追加する
`.insert(i,x)`

```python:

wordlist = ['apple','bike','cake','drum','electric']
wordlist.insert(5,'falcon')
print(wordlist)

##出力結果
['apple', 'bike', 'cake', 'drum', 'electric', 'falcon']

```

引数に設定した値を持つ最初の要素のインデックスを返す
`.index()`

```python:

lst = [1, 2, 3, 4, 5]
idx = lst.index(4)
print(idx)

##出力結果
3

```

リストにもidの概念があるので複製を作るための関数
`.copy()`

```python:

# リストのコピーを作りコピーを編集したいがidが同一になるため失敗する例

wordlist = ['apple','bike','cake','drum','electric']
copylist = wordlist

copylist[0] = 'admin'
print(wordlist)
print(copylist) 

## 出力結果
['admin', 'bike', 'cake', 'drum', 'electric']
['admin', 'bike', 'cake', 'drum', 'electric']

# 成功例

wordlist = ['apple','bike','cake','drum','electric']
copylist = wordlist.copy()
print(wordlist is not copylist) # wordlistはcopylistではないかを検証する

copylist[0] = 'admin'
print(wordlist)
print(copylist) 


##出力結果
True
['apple', 'bike', 'cake', 'drum', 'electric']
['admin', 'bike', 'cake', 'drum', 'electric']

```

mata
2つ以上のリストのループはzipを使うことで表現できる。

例えば以下のような例

```python:

list1 = list(range(1,5))
list2 = list(range(5,9))

for a,b in zip(list1,list2):
    print(f"a = {a}, b = {b}", a, b )

```

カウンタ変数を2つ用意してやり、それぞれにlist1、list2から代入を繰り返していく。
すると以下の通りとなる。

```
##出力結果

a = 1, b = 5 1 5
a = 2, b = 6 2 6
a = 3, b = 7 3 7
a = 4, b = 8 4 8

```



## 命令文の区切り(セミコロン)
Pythonではセミコロンを文末に付ける必要はないが、つけると命令の区切りの表現にできる。
つまり

```python：
a = 10; b=20; c=30;
```
というように変数定義を一列にまとめることができるということ。
もちろん、全部1行にしてしまうと可読性が著しく損なわれるので注意。


## breakとcontinue

Pythonにもbreak及びcontinueはある。

```python:

i = 0

while True:
    i += 1

   if i > 10:
        break
    if i % 2 == 1:
        continue
    print(i)

print('処理を終了します')

```

iが10より上になれば処理は中断、かつiが奇数だった場合、i += 1に処理を返す。
するとprintされるのは10以下の偶数となる。

## タプル型とリスト型

リストはいわゆる配列のようなものだが、それに似たタプル型という概念がPythonにはある。

リストとの違いとしては**格納している要素の更新・追加・削除ができない**というところ.
しかし、逆に言えば定数値を格納してくれるという捉え方ができる。

`list()`及び`tuple()`で他の型からリストまたはタプルに変換することができる。

```python:

a =list("sample"); b=tuple("sample2"); c=list(range(1,11,2)); d=tuple(range(1,11,2))
print(type(d))

```

それぞれ文字列及びrangeによって生成された値(1から始まり2ずつ増分した値を11未満の範囲で取得する)がリスト型及びタプルで格納されている。
ちなみに文字列をリストないしタプルに変換すると1文字ずつにわけて収納される。

リストまたはタプルに格納されている要素を変数に各々代入させたい場合はアンパックが使える。

```python:

words = ['apple','bike','corn']
a,b,c = words
print(a)

##出力結果

apple

```

この際、`words =  a,b,c`であるとエラーになることに注意。

また複数のリストを一つにまとめて二次元リストにしたい場合はzip()を使う。

```python:

a = ['apple','bike','corn']
b = ['docter','egg','falcon']
c = ['game','heart','icon']
d = ['jack','king','love']
array_list = zip(a,b,c,d)
result = list(array_list)
print(result)

##出力結果
[('apple', 'docter', 'game', 'jack'), ('bike', 'egg', 'heart', 'king'), ('corn', 'falcon', 'icon', 'love')]

```

zip()を実行した段階では単なるオブジェクトのままなのでさらにそこからlist関数でリストに変換してやる。
そして、出力結果をみればわかるがzip()を使った場合,
インデックスごとに各リストの要素が再格納されていることがわかる。
つまり、

```python:

print(result[0])

##出力結果
('apple', 'docter', 'game', 'jack')

```

ということになる。
これはfor文でリストを作った場合とは違った処理なので注意。

## セット型

イメージとしては数学用語の集合、つまりグループのようなものだと思っておく。
つまり、重複しない要素の集まりである。
例えば

`fruit = {'apple','banana','lemon'}`

という感じである。
リストが`[]`、タプルが`()`で表現されるのに対して、セットは`{}`で表現する。
しかし、`x = {}`の形を取り、中に要素が登録されていない場合または登録されている要素がキー:値となっているときはセットではなく辞書となる。
また、セットはタプルは登録できるが、リストは登録できない。

## リスト・タプル・セット・辞書ののまとめ



リスト

・いわゆる配列。
・要素の更新、追加、削除が可能。
・追加は`append()`、削除は`remove()`。
・他の型からリストへの変換は`list()`。

タプル

・定数値を格納してる配列というイメージ。
・要素の更新、追加、削除は不可。
・他の型からタプルへの変換は`tuple()`。

セット

・いわゆる集合配列(グループ)。
・同じ内容の要素を重複して登録することはできない。
・タプルは要素として取れるが、リストは不可。
・他の型からセットへの変換は`set()`。
・リストやタプルと違って整列順序がないので、**インデックスが存在しない。**。
・追加は`add()`、削除は`remove()`。
・セットは要素の更新、追加、削除ができるが、同じセットでもそれが不可な型も存在する。

辞書

・いわゆる連想配列。
・要素はキー:値の形でなければならない。
・`keys()`でキーの取り出し、`value()`で値の取り出し、`items()`で双方同時に取得。

ということになる。

## randomモジュールの補足

randomモジュールは数値だけではなく、タプルやリストなどのシーケンスにも使える。

`choice()`でシーケンスからランダムに要素を一つ取得する。
`choices(seq, k = 1)`で引数に設定したシークエンスから重複ありでk個の要素を取得し、それをリストにして返す。
`sample(seq,k)`は重複無しで、シークエンスからk個の要素を取得し、それをリストにして返す。
`shuffle()`でシーケンスを並び替えるとなる。

つまり、

```python:

import random

a = ['apple','basket','color','dictionary','error']

wordlist =list(a)

print(random.choice(wordlist))
print(random.choices(wordlist,k=3))
print(random.sample(wordlist,3))

print(wordlist)
random.shuffle(wordlist)
print(wordlist)

##出力結果(randomモジュールなので実行の度に結果は変わる)

dictionary
['error', 'apple', 'color']
['dictionary', 'basket', 'color']
['apple', 'basket', 'color', 'dictionary', 'error']
['color', 'apple', 'dictionary', 'error', 'basket']

```

## 内包表記

```python:

squares = [v ** 2 for v in range(10)]
print(squares)

```
前回やった通り、上記のようにリスト内でfor文を作り、forの前にリストに繰り返し格納したい要素の式を置くと、数列などは簡単につくることができる。
例えば上記の例であると0~9の2乗の値がリストに格納されていくことになる。
これはセット型でも使えて、その場合集合を生成することができる。

```python:

sample = {x for x in range(30) if x %2 == 0}
print(sample)

##出力結果
{0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28}

```

集合を生成するという観点からいうと単なるリストと区別するためにも、生成するものには規則性があるべき(上記は0≦x<30の範囲の偶数の集合ということになる)。

辞書の場合はこうなる。

```python

languages = [
    "apple",
    "bike",
    "cow",
    "decade",
    "egostic"
]

dictionary = {lang.upper():len(lang) for lang in languages}
print(dictionary)

##出力結果
{'APPLE': 5, 'BIKE': 4, 'COW': 3, 'DECADE': 6, 'EGOSTIC': 7}

```

まずはリストでキーを作る。
これはタプルでも問題ない。
そしてforの前に`key:value`の形を定義する。
今回は作ったリストをカウンタ変数`lang`に代入する形を取る。
そして、その`lang`をもとに`languagesに格納されている文字列の大文字:その文字数の値`としてプロパティを作るというのを繰り返して辞書を作るということになる。



##DockerでJupyterLabの環境作る

###構築手順

####1. Dockerfileの作成

```
FROM jupyter/datascience-notebook:7a0c7325e470 # イメージの指定

COPY requirements.txt tmp/requirements.txt # インストールするパッケージを列挙したファイル。
RUN pip install -r tmp/requirements.txt # pipはPythonのパッケージをインストールするためのコマンド。ここではrequirements.txtに列挙されているパッケージをインストールする。

EXPOSE 8888

```

おさらいとしてFROMはベースイメージの選択、つまり使いたいイメージを指定すること。
EXPOSEは指定したポートの開放。もちろん、MacまたはWindows10 ProでDocker環境を構築していない場合はVirtual Box側のポートフォワーディングも設定しなければならない。


####2. docker-compose.ymlを作る

```yml

version: "3.7" # docker-compose.ymlのファイルフォーマットのバージョン指定
services:
  jupyterlab: # 扱うサービス
    build: .
    container_name: jupyterlab # コンテナ名
    environment:
      - JUPYTER_ENABLE_LAB=yes # jupyterLabを有効にする
    ports:
      - "8888:8888" # サービスで扱うポートの指定
    volumes:
      - ./JupyterLab:/home/jovyan/work # 共有・同期させるディレクトリの設定。ホスト側のディレクトリ:仮想空間側のディレクトリ
    command: start.sh jupyter lab --NotebookApp.token='' # コンテナ起動時にログインtokenの入力を省略する


```
Dockerに関してはまだまだ不慣れなので色々ググりながらやってみたところ、docker-compose.ymlを作る意味としては`docker run`でやろうとするとあとに長々とオプションをしてしないといけないところわかりやすくするという意味合いがあるようだ。
そして、最も手っ取り早く環境構築するには公式からimageをpullして`docker run`でオプションをつけてコンテナを作り、そこで起動するというのが1番なのだが、やはりこれはDockerに慣れた人でないと沼にはまる原因だと感じたし、Dockerfile(最近はこれも書かない場合もあるそうな)・docker-compose.ymlの2ファイルでコンテナを管理するほうが逆に後々可読性や拡張のやりやすさの点からいいのではと私は思いました。

## JupyterLabを使いながらPythonでのファイルの読み書きを行う。

#### 文字コード確認

```python:

import sys
print(sys.getdefaulttencoding())

```

#### ファイルの読み込み

```python:

with open('jinko.txt','rt')as f:
    print(f.read())

```

`open()`から始まり、`close()`で終わるのがPythonでのファイルの読み書きだが`close()`忘れを防ぐために、`with open()`とする。

#### ファイルのデータをリストとして読み込む

```python:

with open('jinko.txt') as f:
    l = f.readlines()
print(l)

# または

with open('jinko.txt') as f:
    while True:
        l = f.readline()
        print(l)
        if not l:
            break

```

ファイルを変数fに代入し、オブジェクト化してreadlines()メソッドを使う。
それかループ処理でreadline()メソッドを使って1行ずつ読み込んでいく。

#### whileで読み込む行数を制限する

```python:

count = 0
with open('jinko.txt') as f:
    l = f.readline()
    while True:
        l = f.readline()
        m = l.strip()
        print(m)
        count += 1
        if count > 5:
            break

# カンマ区切りにして1行ずつリストにする場合

import csv

count = 0
with open('jinko.txt') as f:
    l = f.readline()
    reader = csv.reader(f)
    for l in reader:
        print(l)
        count += 1
        if count > 5:
            break

# データから数値を取り出して計算してみたりする

import csv

s = 0
with open('jinko.txt') as f:
    reader = csv.reader(f)
    for l in reader:
        if l[0] == "北海道":
            s = s + int(l[2])
print("北海道の人口", s ,"人")

## 上記で読み込むデータの一部抜粋

['北海道', '札幌市', '1913545']
['北海道', '函館市', '279127']
['北海道', '小樽市', '131928']
['北海道', '旭川市', '347095']
['北海道', '室蘭市', '94535']
['北海道', '釧路市', '181169']


```

`csv.reader()`は`split()`と同じような働きをする。
CSV形式のデータであればこちらを使うことが推奨される。
計算の部分についてはまず読み込むデータの抜粋から`l[0]`は都道府県を指す、つまり`if l[0] == "北海道":`で読み込みたいデータの範囲を決める。
そして`l[2]`は人口、つまり数のデータが入っていることがわかる。
ただし、文字列として格納されているので`int()`で数値化してから使う。
よって上記の文を書くとデータ内の北海道の人口を算出できるということになる。


#### エンコードが違うファイルを読み込む

```python:

with open('jinko_sjis.txt', encoding='shift-jis', newline='\r\n') as f:
    while True:
        l = f.readline()
        print(l)
        if not l:
            break

```

utf-8以外のファイルを読み込むときはオプションで`encoding = '読み込みたいファイル形式'`と指定する。
また、`newline`で改行の種類を指定する。
今回はWindowsでの改行の指定をしている。

#### ファイルの書き込み

```python:

with open("write_example.txt", "w") as f:
    f.write("abc\r\n")
    f.write("defgh")

# 複数行データの書き込み

data = ["abc","defgh"]
with open("write_example2.txt","w") as f:
    f.writelines(data)

# CSVデータの書き込み

import csv

alldata = [
    [1,2,3],[2,3,5]
]

with open("write_example3","w") as f:
    writer =csv.writer(f)
    writer.writerows(alldata)


## for文も使える


import csv

alldata = [
   [1, 2, 3],
   [2, 3, 5]
]

with open("writeexample5.txt", "w") as f:
    writer = csv.writer(f)
    for data in alldata:
            writer.writerow(data)


```

## 参考サイト

[Dockerを使って機械学習の環境を作ろうとした話](https://qiita.com/penpenta/items/3b7a0f1e27bbab56a95f)

[JupyterLabのコンテナ環境をつくる](https://goodbyegangster.hatenablog.com/entry/2020/01/11/071107)