きり丸の技術日記

技術検証したり、資格等をここに残していきます。

PyCharmでJupyter Notebookを設定するだけの記事

始めに

最近、回帰分析をやるようになりました。分析そのものはまだまだ知識不足なものの、とりあえずPyCharmで分析を可視化するJupyter Notebookを使用する方法がわからなかったのでメモします。

なお、本当に初心者向けの記事となります。

環境

  • PyCharm
    • 2025.1.1.1

実装

リモートサーバを起動する

PyCharmはデフォルトでJupyter Notebookは使用できる状態を提供してくれます。しかし、私の環境が悪いのか、matplot等の追加でライブラリをインストールしたいときにうまくインストールできませんでした。そのため、ローカルにJupyter Labのサーバを起動して、PyCharmはそのサーバを参照するようにします。

# Jupyter インストール + 追加でインストールしたいライブラリ
uv add jupyter matplot
## もし、グルーピングしたいならこちらを使用する
uv add --group analyze jupyter matplot

# ローカルサーバの起動
## 起動するたびにトークンが変わってしまうので、パラメータとして渡すのがオススメです
uv run jupyter lab --ServerApp.token="test_token"

PyCharmで参照させる

適当にJupyter Notebookのファイルを作成してください。右クリックからNew -> Jupyter Notebookでもいいですし、ipynbの拡張子のファイルを作ってください。

その後、右上にJupyter Notebookの設定をする箇所があるので、Configure Jupyter Serverを選択してください。

サーバを追加してConfigure Serverを選んでください。

設定にhttp://localhost:8888と起動時に設定したトークンのtest_tokenを設定すれば実行可能です。

ソースコード

なし

終わりに

Webアプリケーションのことなら経験があるのでわかるのですが、急に分析タスクをアサインされて困りました。

最初はよくわかっていなかったので、とりあえずGoogle スプレッドシートでの分析を行っていました。このGoogleスプレッドシートの回帰分析については後ほど簡単ですがブログにします。ある程度方針が見えてきて、Pythonに分析手法をお引越しさせたところ、Jupyter Notebookなら簡単に可視化できるとのことでやってみたのですが、微妙に設定方法ががわからなくてハマってしまいました。

アプリケーションばっかり触ってきて、分析したことがない人にとっては、少しは役に立つ記事になったのではないかと思います。

参考情報

Pydanticで型定義を流用したら正常動作しなくなった(シリアライズ)

始めに

結果が分かれば単純なミスでした。Pydanticで型定義しただけと考えていましたが、少しでも振る舞いが変わるときはちゃんと確認しましょう。

環境

  • Python
    • 3.13
  • Pydantic
    • 2.11.4

実装

変更前のコード

SaaS企業が外部連携しやすいようにライブラリを公開していることがあります。基本的にはライブラリ内で型定義はすでに行われている状態ですが、SaaS側でユーザが自由にプロパティを追加・修正できる項目がある場合には、該当プロパティを参照及び更新するためには自前でカスタマイズして連携する必要があります。今回発生したケースでもその自前カスタマイズをする必要があったのですが、コード上で特に型定義もせずに直接dictで設定していました。

from typing import Any
from pydantic import BaseModel

class Update(BaseModel):
  id: int
  data: Any 

record = Update(
  id=1,
  data={
    "id": "aaa",
    "value": "ccc",
    "pattern": 1,
    "now": "2025-05-16T17:32:30.204923+09:00"
  }
)

# record インスタンスをもとに 外部ライブラリに連携する処理

変更後のコード

型がないと横展開時の漏れに気づけなかったり、必要なパラメータが足りているのか判断が難しくなるため、今後の拡張性を見越して型定義をしました。リファクタリングとしてpydanticで型定義しつつ、 model_dumpを使用することでdictで渡せるように変更しました。

from typing import Any
from datetime import datetime
from enum import IntEnum
from pydantic import BaseModel

class PatternEnum(IntEnum):
    A = 0
    B = 1    
    
class CustomModel(BaseModel):
    id: str
    value: str
    pattern: PatternEnum
    now: datetime

class Update(BaseModel):
  id: int
  data: CustomModel

record = Update(
  id=1,
  data=CustomModel(
    id="aaa",
    value="ccc",
    pattern=1,
    now=datetime.now()
  ).model_dump()
)

# record インスタンスをもとに 外部ライブラリに連携する処理

ただし、これではうまく動きませんでした。原因としては、SaaSと連携するために最終的にシリアライズしてデータ連携する必要があるのですが、datetimeをシリアライズしたタイミングで次のエラーが出るからです。

*** TypeError: Object of type datetime is not JSON serializable

対策としてmode='json'を付与することで、シリアライズも行えるようになっています。

{'id': 'aaa', 'value': 'ccc', 'pattern': 1, 'now': '2025-05-16T17:44:50.670465+09:00'}

また、ライブラリ側の型ヒントがdictではなく、strでも問題ない場合はmodel_dump_jsonを使用するといいでしょう。

'{"id":"aaa","value":"ccc","pattern":1,"now":"2025-05-16T17:44:50.670465+09:00"}'

ソースコード

実際のテストコードで問題と解決策を確認できます: - GitHub: test_to_json.py - Pydanticモデルのシリアライズテスト

まとめ

外部ライブラリに連携する際には、model_dumpmode=jsonを使用すると安心。 または、文字列でよければmodel_dump_jsonを使用する。

終わりに

リファクタリングと言いつつ、strdatetimeを型変換している箇所が悪いんですけどね。元々、一旦DBに格納した情報をSaaS側に反映する用途で使用していたため、DBで使用していた型をそのまま流用してしまっていました。流用すること自体は問題ないのですが、差分を理解しないまま流用してはいけませんね。

特に、Pydanticで型定義を導入することは開発効率とコードの品質向上に大きく貢献しますが、型変換の過程でデータがどのように変化するかを理解しておくことが重要です。今回はシンプルなケースでしたが、これを機にPydanticのmodel_dumpmodel_dump_jsonの挙動についても理解を深めることができました。単純ですが勉強になりました。

uvでライブラリをグルーピングして最小限の依存関係で起動できるようにする

始めに

Pythonに限らずアプリケーションを大規模で運用していくほどライブラリの依存関係が増えていきます。その時にAPIとバッチ等の用途に応じたアプリケーションごと分割する方法もありますが二重管理が面倒になります。JavaではGradleを使用してマルチプロジェクト構成にしておくことで、必要なライブラリだけで起動するようにできました。

今回の記事では、Pythonのuvを使用してライブラリをグルーピングすることで、必要なライブラリを最小限にする方法を記載します。

環境

  • Python
    • 3.13
  • uv
    • 0.7.2

実装

ライブラリのグルーピング

dependency-groupsに分割したい単位で集約します。今回の場合は、dependenciesで全アプリケーションに依存させたいライブラリを残し、他はapi, auth, worker, devに分割しました。

[project]
dependencies = [
    "sqlmodel>=0.0.21",
    "pydantic[email]>=2.9.2",
]

[dependency-groups]
api = [
    "fastapi[standard]>=0.115.12"
]

auth = [
    "pyjwt>=2.8.0",
    "pyotp>=2.9.0",
]

worker = [
    "celery>=5.5.2",
]

dev = [
    "ruff>=0.7.3"
]

なお、該当のグループにライブラリを追加するときは次のコマンドで入れられます。下手に手でpyproject.tomlをいじるより、uvに任せてグループを作ってから少しずつ移行するのがオススメです。

uv add --group dev pytest

使用方法

uv sync時に必要なグループだけ指定してください。なお、uvの仕様でdevは特に指定がない限りインストールされるので、明示的な除外が必要です。

# api (+dev)だけインストール
uv sync --group api

# api と auth (+dev)だけインストール
uv sync --group api --group auth

# dev を除外したい
uv sync --no-group dev

# 全部対象
uv sync --all-groups

なお、開発環境でグループを指定してインストールさせる指定をするのは面倒なので、pyproject.tomlにデフォルトインストール対象も記載しておくと便利だと思います。

[tool.uv]
# 全部指定する場合
default-groups = "all"
# インストール対象を絞りたいときに
#default-groups = ["api", "auth", "dev"]
uv sync

ソースコード

終わりに

Pythonはインストールイメージのサイズが大きくなりがちなので、こういうことでサイズを削減したいと考えています。ただ、ライブラリ管理を厳密にやったことで実行時に必要なライブラリが読み込まれていないエラーが発生する可能性もあるので、実際に本番運用に適用するかは悩んでいます。

ただ、バッチ処理ではFastAPIは不要ですし、少しでも起動が早いほうが正義だと思うので、CI/CDの管理工数も考えて本番にも取り入れていきたいです。

参考情報