プログラミング備忘録

初級プログラマ。python、DL勉強中

sample

1

from abc import ABC, abstractmethod


# インターフェース(抽象)
class IUserRepository(ABC):
    @abstractmethod
    def save(self, name: str) -> None:
        # ここには具体的な処理は書かない(passで何もしない)
        pass

# 実体その1:MySQLに保存するクラス
class MySQLDatabase(IUserRepository):
    def save(self, name: str) -> None:
        print(f"MySQLに「{name}」を保存しました!")

# 実体その2:テスト用にメモリに仮保存するクラス
class MockDatabase(IUserRepository):
    def save(self, name: str) -> None:
        print(f"テスト環境のメモリに「{name}」を仮保存しました!")


class UserService:
    # クラスを作るときに、実体を外から受け取る
    def __init__(self, repository: IUserRepository):
        self.repository = repository

    # ユーザー登録のメインロジック
    def register_user(self, name: str) -> None:
        print("名前の文字数チェックなど、ビジネスルールの処理...")
        
        # 抽象レイヤーの機能を使う(裏で何が動いているかは気にしない)
        self.repository.save(name)



# ① 本番環境で動かす場合
# MySQLの実体を作って、ビジネスロジックに渡す
production_db = MySQLDatabase()
production_service = UserService(production_db)

print("【本番環境の実行】")
production_service.register_user("山田太郎")
print("-" * 30)

# ② テスト環境で動かす場合
# テスト用の実体を作って、ビジネスロジックに渡す
test_db = MockDatabase()
test_service = UserService(test_db)

print("【テスト環境の実行】")
test_service.register_user("佐藤花子")




(外界) Webブラウザ・スマホアプリ
  │
  ▼
【① コントローラー(追加!)】 ★外界との窓口(左側)
  │ 「WebからのJSONデータを受け取って、UserServiceに渡すよ」
  │
  ▼
【② ユースケース(UserService)】 ★シナリオ進行役
  │ 「登録処理を始めるぞ!」
  │
  │ ──? 【③ エンティティ(追加!)】 ★不変のルール
  │      「名前は3文字以上かチェックするぞ!(UserNameクラスなど)」
  │
  ▼
【④ 抽象レイヤー(IUserRepository)】 ★ユースケースが作った契約書
  ▲
  │ (依存の逆転・import)
  │
【⑤ DBアダプター(MySQLDatabase)】 ★外界との窓口(右側)
  │ 「契約書通りに、MySQL用のSQLを発行するよ」
  │
  ▼
(外界) 本物のMySQLデータベース

2

from abc import ABC, abstractmethod
from dataclasses import dataclass

# ==========================================
# 1. Domain Model (ドメインモデル)
# ==========================================
@dataclass
class User:
    user_id: str
    name: str
    email: str  # メールアドレスを追加


# ==========================================
# 2. 抽象化レイヤー (Ports: ポート)
# ※ここが「USBの差し込み口」になります
# ==========================================
# ① データベース用のインターフェース
class IUserRepository(ABC):
    @abstractmethod
    def find_by_name(self, name: str) -> User | None:
        pass

    @abstractmethod
    def save(self, user: User) -> None:
        pass

# ② 【NEW】メール送信用インターフェース
class IEmailSender(ABC):
    @abstractmethod
    def send_welcome_email(self, email_address: str, name: str) -> None:
        pass


# ==========================================
# 3. Domain Service (ドメインサービス)
# ==========================================
class UserDuplicationService:
    def __init__(self, repository: IUserRepository):
        self.repository = repository

    def is_duplicated(self, name: str) -> bool:
        user = self.repository.find_by_name(name)
        return user is not None


# ==========================================
# 4. Application Service (ユースケース層)
# ※ここがシステムの中核(マザーボード)
# ==========================================
class UserRegistrationAppService:
    # データベースとメール送信、2つの「抽象(ルール)」を受け取る!
    def __init__(
        self, 
        repository: IUserRepository, 
        dup_check_service: UserDuplicationService,
        email_sender: IEmailSender # NEW: メール送信機能を追加
    ):
        self.repository = repository
        self.dup_check_service = dup_check_service
        self.email_sender = email_sender

    def register(self, user_id: str, name: str, email: str) -> None:
        user = User(user_id=user_id, name=name, email=email)

        if self.dup_check_service.is_duplicated(user.name):
            print(f"? エラー: 「{name}」さんは既に登録されています!")
            return

        # インフラへの命令(詳細は知らずに、ただルール通りに呼び出すだけ)
        self.repository.save(user)
        self.email_sender.send_welcome_email(user.email, user.name)
        
        print(f"?? 成功: 「{name}」さんの登録処理がすべて完了しました!\n")


# ==========================================
# 5. Infra (インフラ層 / Adapters: アダプター)
# ※ここが「USBに挿す周辺機器」になります
# ==========================================
# ① DBの実体(メモリに保存)
class InMemoryUserRepository(IUserRepository):
    def __init__(self):
        self.db: dict[str, User] = {}

    def find_by_name(self, name: str) -> User | None:
        for user in self.db.values():
            if user.name == name:
                return user
        return None

    def save(self, user: User) -> None:
        self.db[user.user_id] = user
        print(f"  [DBログ] データベースに {user.name} を保存しました。")

# ② 【NEW】メール送信の実体(今回は画面に文字を出すだけ)
class ConsoleEmailSender(IEmailSender):
    def send_welcome_email(self, email_address: str, name: str) -> None:
        print(f"  [Mailログ] {email_address} 宛てに、{name}さんへの歓迎メールを送信しました!")


# ==========================================
# 6. UI (プレゼンテーション層)
# ==========================================
class UserController:
    def __init__(self, app_service: UserRegistrationAppService):
        self.app_service = app_service

    def handle_register_request(self, user_id: str, name: str, email: str) -> None:
        print(f"【UI】登録リクエスト受付: ID={user_id}, Name={name}")
        self.app_service.register(user_id, name, email)


# ==========================================
# メインプログラム (コンポジションルート)
# ==========================================
if __name__ == "__main__":
    # 1. 道具(インフラ周辺機器)の準備
    repository = InMemoryUserRepository()
    email_sender = ConsoleEmailSender() # メール送信機を用意

    # 2. ビジネスルールの準備
    dup_service = UserDuplicationService(repository)

    # 3. 進行役(マザーボード)に周辺機器をすべてセット!
    app_service = UserRegistrationAppService(repository, dup_service, email_sender)

    # 4. 受付窓口(UI)の準備
    ui = UserController(app_service)

    # --- 実行テスト ---
    ui.handle_register_request("001", "山田太郎", "yamada@example.com")






my_project/
 │
 ├─ domain/                 # ドメイン層(システムの中核)
 │   ├─ models.py           # エンティティ(User)
 │   ├─ interfaces.py       # 抽象化レイヤー(IUserRepositoryなど)
 │   └─ services.py         # ドメインサービス(UserDuplicationService)
 │
 ├─ application/            # ユースケース層
 │   └─ services.py         # アプリケーションサービス(UserRegistrationAppService)
 │
 ├─ infrastructure/         # インフラ層(外の世界)
 │   ├─ repositories.py     # DB実体(InMemoryUserRepository)
 │   └─ email.py            # メール実体(ConsoleEmailSender)
 │
 ├─ presentation/           # UI層(入力を受け取る)
 │   └─ controllers.py      # UI実体(UserController)
 │
 └─ main.py                 # メインプログラム(構成ルート・一番外側)

OpenAPI (Swagger)

編集ツール

editor.swagger.io

サンプル

openapi: 3.0.0

info:
  title: Sample API
  description: A short description of API.
  version: 1.0.0

paths:
  # paths オブジェクト
  /users:
    # path item オブジェクト
    get: # GET
      # Operationオブジェクト
      tags:
        - users
      summary: Get all users.
      description: Returns an array of User model
      parameters: []
      responses: # レスポンス定義
        '200': # HTTP status
          description: A JSON array of User model
          content:
            application/json: # レスポンスの形式指定
              schema: 
                type: array
                items:
                  $ref: '#/components/schemas/User' # 参照するモデル
                example: # サンプルデータ
                  - uid: 1
                    name: John Doe
                  - uid: 2
                    name: Jane Doe
    post: # POST
      tags: 
        - users
      summary: Create a new User
      description: Create a new User
      parameters: []
      requestBody: # リクエストボディ
        description: user to create
        content:
          application/json:
            schema: # POSTするオブジェクト
              $ref: '#/components/schemas/User'
            example:
              uid: 3
              name: Richard Roe
      responses:
        '201':
          description: CREATED
  /users/{userId}:
    get:
      tags:
        - users
      summary: Get user by ID.
      description: Returns a single User model
      parameters: # リクエストパラメータ
        - name: userId
          in: path # パラメータをパス内に含める
          description: user id
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: A single User model
          content:
            application/json:
              schema: 
                type: object
                items:
                  $ref: '#/components/schemas/User'
                example:
                  uid: 1
                  name: John Doe
                  
  /products:
    # path item オブジェクト
    get: # GET
      # Operationオブジェクト
      tags:
        - products
      summary: Get all products.
      description: Returns an array of Products model
      parameters: []
      responses: # レスポンス定義
        '200': # HTTP status
          description: A JSON array of Product model
          content:
            application/json: # レスポンスの形式指定
              schema: 
                type: array
                items:
                  $ref: '#/components/schemas/Product' # 参照するモデル
                example: # サンプルデータ
                  - pid: 1
                    name: tablet
                  - pid: 2
                    name: iphone
    post: # POST
      tags: 
        - products
      summary: Create a new User
      description: Create a new User
      parameters: []
      requestBody: # リクエストボディ
        description: user to create
        content:
          application/json:
            schema: # POSTするオブジェクト
              $ref: '#/components/schemas/Product'
            example:
              pid: 3
              name: disc
      responses:
        '201':
          description: CREATED

                  
                  
components:
  schemas: # スキーマオブジェクトの定義
    User: # モデル名
      type: object # 型
      required: # 必須フィールド
        - uid
      properties:
        uid: # プロパティ名
          type: integer # 型 
          format: int64 # フォーマット(int32, int64等)
        name:
          type: string
    Product:
      type: object
      required:
        - pid
        - price
      properties:
        pid:
          type: integer
          format: int64
          example: 1
        name:
          type: string
          example: Laptop
        price:
          type: integer
          example: 1200

参考

qiita.com

qiita.com

Node.jsをUbuntuにローカルインストール

nvm

Node.jsをダウンロードしてインストールできるツール。

バージョンの切替方法は以下
qiita.com

npm

javascriptパッケージをインストールするツール。

インストール

以下を実行

git clone https://github.com/creationix/nvm.git ~/.nvm
source .nvm/nvm.sh
nvm install 10.15.3    #最新版

設定

bashrcもしくはbash_profileに以下を追記

if [[ -s ~/.nvm/nvm.sh ]];
 then source ~/.nvm/nvm.sh
fi

確認

hello.jsを作成

console.log("hello node.js")

hello.jsを実行

$ node hello.js 
hello node.js

参考

UbuntuにNode.jsをインストールする - Qiita

bashからpsqlを非対話で使う

#!/bin/bash

DATABASE="testcopy"
DIRPATH="/hogehoge/postgres/files/"

### database作成
psql -c "create database ${DATABASE}"

### テーブル定義
psql ${DATABASE} < ${DIRPATH}/ddl/s.txt
psql ${DATABASE} < ${DIRPATH}/ddl/s2.txt
psql -c "\c ${DATABASE}" -c "\dt"

### データのロード
psql -c "\c ${DATABASE}" -c "\copy mytable  from ${DIRPATH}/csv/1.csv with csv header ;"
psql -c "\c ${DATABASE}" -c "\copy newtable from ${DIRPATH}/csv/4.csv with csv header ;"

### 確認
psql -c "\c ${DATABASE}" -c "select * from mytable ;"
psql -c "\c ${DATABASE}" -c "select * from newtable;"


postgreSQLのメモ

初期設定

ユーザー切り替え(ログイン)

sudo -i -u postgres

ダンプ

pg_dump {database} -t {table} -U postgres -s --file={filename}

復元

psql {database} < s.txt

接続(対話型)

psql {database}

接続時

  • \l ## データベース一覧
  • \q ## 終了
  • \c {database} ## データベースの選択
  • \d {table} ##テーブル詳細

DDL

  • create/drop database {databasename};
  • create/drop table {tablename}
test=# create table mytable (
test(# id integer, 
test(# name varchar(10)
test(# );
  • drop table {tablename}

DML

  • insert into mytable values (1, 'hogehoge');
  • select * from mytable;
  • delete from mytable

その他

\copy mytable from /path/1.csv with csv header ;

備考

  • 初期DBは制御用、templete0は接続不可
 postgres        
 template0   
 template1 

data cluster

  • 作成・開始・終了
/usr/lib/postgresql/10/bin/initdb  -E UTF-8 --no-locale -D   /home/develop/db/posgretest

/usr/lib/postgresql/10/bin/pg_ctl -D /home/develop/db/posgretest/  start
/usr/lib/postgresql/10/bin/pg_ctl -D /home/develop/db/posgretest/ stop

psql -l

https://www.postgresql.jp/document/9.1/html/creating-cluster.html

http://tihiro.hatenablog.com/entry/2017/08/08/022739

sarimax

import pandas  as pd
import statsmodels.api as sm

import matplotlib
import matplotlib.pyplot as plt


### pandas setting
#pd.get_option("display.max_columns")
#pd.set_option('display.max_columns', 5)
pd.set_option("display.max_colwidth", 80)

### pyplot setting 

plt.style.use('ggplot')
#font = {'family' : 'meiryo'}
#matplotlib.rc('font', **font)

plt.rcParams["figure.figsize"] = [10,10]


df=pd.read_csv("./data/tokyo-2016.csv")
df["TIMESTAMP"]=pd.to_datetime(df["DATE"])
#print(df["TIMESTAMP"])

### グループ化
df_sum=df.groupby("TIMESTAMP")["POWER"].sum().reset_index()
df_sum=df_sum.sort_values("TIMESTAMP")
df_sum= df_sum.set_index(['TIMESTAMP'])
#print(df_sum)

### plot
#df_sum.plot(alpha=0.6)
#plt.title("timeplot")
#plt.savefig("./timelog/plot.png")

### トレンド、季節性、ノイズ
res = sm.tsa.seasonal_decompose(df_sum)
#res.plot()
#plt.title("trend")
#plt.savefig("./timelog/trend.png")

observed=res.observed["POWER"]
seasonal=res.seasonal["POWER"]
trend=res.trend["POWER"]
resid=res.resid["POWER"]

df_new=pd.DataFrame(index=[])
df_new["observed"]=observed
df_new["trend"]=trend
df_new["seasonal"]=seasonal
df_new["resid"]=resid

#print(df_new)


df_new["check"]=df_new["trend"]+df_new["seasonal"]+df_new["resid"]
#print(df_new)

### 単位値検定
# トレンド項あり、定数項あり
ct = sm.tsa.stattools.adfuller(df_sum["POWER"], regression="ct")
# トレンド項なし、定数項あり
c = sm.tsa.stattools.adfuller(df_sum["POWER"], regression="c")
# トレンド項なし、定数項なし
nc = sm.tsa.stattools.adfuller(df_sum["POWER"], regression="nc")
# 確認
print("diff-0: ", ct[1],c[1],nc[1])


df_diff1=df_sum.diff(1)
diff = df_diff1.dropna(inplace=True)
# トレンド項あり、定数項あり
ct = sm.tsa.stattools.adfuller(df_diff1["POWER"], regression="ct")
# トレンド項なし、定数項あり
c = sm.tsa.stattools.adfuller(df_diff1["POWER"], regression="c")
# トレンド項なし、定数項なし
nc = sm.tsa.stattools.adfuller(df_diff1["POWER"], regression="nc")
# 確認
print("diff-1: ", ct[1],c[1],nc[1])

#df_diff1.plot()
#plt.title("diff1")
#plt.savefig("./timelog/diff1.png")

#print(df_sum)

df_train= df_sum[df_sum.index < "2016-12"]
print(df_train)
df_test = df_sum[df_sum.index >= "2016-12"]
print(df_test)

def paramselect(df_train):
    max_p = 3 #AR
    max_q = 3 #MA
    max_d = 2 #diff
    max_sp = 1 #seasonal AR
    max_sq = 1 #seasonal MA
    max_sd = 1 #seasonal diff

    pattern = max_p*(max_d + 1)*(max_q + 1)*(max_sp + 1)*(max_sq + 1)*(max_sd + 1)

    modelSelection = pd.DataFrame(index=range(pattern), columns=["model", "aic"])

    # 自動SARIMA選択
    num = 0

    for p in range(1, max_p + 1):
        for d in range(0, max_d + 1):
            for q in range(0, max_q + 1):
                for sp in range(0, max_sp + 1):
                    for sd in range(0, max_sd + 1):
                        for sq in range(0, max_sq + 1):
                            sarima = sm.tsa.SARIMAX(
                                df_train, order=(p,d,q),
                                seasonal_order=(sp,sd,sq,12),
                                enforce_stationarity = False,
                                enforce_invertibility = False
                            ).fit()
                            modelSelection.ix[num]["model"] =  \
                                "order=(" + str(p) + ","+ str(d) + ","+ str(q) + "), \
                                season=("+ str(sp) + ","+ str(sd) + "," + str(sq) + ")"
                            modelSelection.ix[num]["aic"] = sarima.aic
                            num = num + 1

    print(modelSelection.sort_values(by='aic').head())

### parameter select
#paramselect(df_train)

### SARIMAX model
model = sm.tsa.SARIMAX(df_train, order=(3,0,3), seasonal_order=(1,1,1,12),
                        enforce_stationarity=False,enforce_invertibility=False).fit()
ljungbox_result = sm.stats.diagnostic.acorr_ljungbox(model.resid, lags=10)
df_ljungbox_result = pd.DataFrame({"p-value":ljungbox_result[1]})
df_tmp=df_ljungbox_result[df_ljungbox_result["p-value"] <  0.05]
print(df_tmp)


### pred(overfitting)
pred=model.predict('2016-05-01', '2016-11-30')
plt.figure()
plt.plot(df_train, label="train" ,color="blue")
plt.plot(pred, "r", label="pred" ,color="red")
plt.legend()
plt.title("sarimax_overfit")
plt.savefig("./timelog/sarimax_overfit.png")


### pred
pred=model.predict('2016-11-30', '2016-12-31')
plt.figure()
plt.plot(df_train, label="train" ,color="blue")
plt.plot(pred, "r", label="pred" ,color="red")
plt.legend()
plt.title("sarimax_train")
plt.savefig("./timelog/sarimax_pred.png")


### predcheck
pred=model.predict('2016-11-30', '2016-12-31')
plt.figure()
plt.plot(df_test, label="test" ,color="blue")
plt.plot(pred, "r", label="pred" ,color="red")
plt.legend()
plt.title("sarimax_test")
plt.savefig("./timelog/sarimax_check.png")

参考