From a862c49c471272c5f6a29d8adc0b326fc9b2ee88 Mon Sep 17 00:00:00 2001 From: myoshizumi Date: Thu, 26 Feb 2026 10:04:57 +0900 Subject: [PATCH 01/25] Add 1193. Monthly Transactions I and update notebook kernelspec --- .../Sherlock_and_Divisors.ipynb | 8 +- .../Monthly_Transactions_I.html | 1938 +++++++++++++++++ .../Monthly_Transactions_I_pandas.md | 202 ++ .../Monthly_Transactions_I_postgresql.md | 182 ++ .../Monthly_Transactions_I.html | 1938 +++++++++++++++++ public/index.html | 14 +- 6 files changed, 4275 insertions(+), 7 deletions(-) create mode 100644 SQL/Leetcode/Intermediate Select/1193. Monthly Transactions I/Claude Sonnet 4.6 Extended/Monthly_Transactions_I.html create mode 100644 SQL/Leetcode/Intermediate Select/1193. Monthly Transactions I/Claude Sonnet 4.6 Extended/Monthly_Transactions_I_pandas.md create mode 100644 SQL/Leetcode/Intermediate Select/1193. Monthly Transactions I/Claude Sonnet 4.6 Extended/Monthly_Transactions_I_postgresql.md create mode 100644 public/SQL/Leetcode/Intermediate Select/1193. Monthly Transactions I/Claude Sonnet 4.6 Extended/Monthly_Transactions_I.html diff --git a/Mathematics/Fundamentals/HackerRank/Claude/Easy/Sherlock and Divisors/Sherlock_and_Divisors.ipynb b/Mathematics/Fundamentals/HackerRank/Claude/Easy/Sherlock and Divisors/Sherlock_and_Divisors.ipynb index a84b1f40..933a88ff 100644 --- a/Mathematics/Fundamentals/HackerRank/Claude/Easy/Sherlock and Divisors/Sherlock_and_Divisors.ipynb +++ b/Mathematics/Fundamentals/HackerRank/Claude/Easy/Sherlock and Divisors/Sherlock_and_Divisors.ipynb @@ -63,8 +63,14 @@ } ], "metadata": { + "kernelspec": { + "display_name": ".venv", + "language": "python", + "name": "python3" + }, "language_info": { - "name": "python" + "name": "python", + "version": "3.12.4" } }, "nbformat": 4, diff --git a/SQL/Leetcode/Intermediate Select/1193. Monthly Transactions I/Claude Sonnet 4.6 Extended/Monthly_Transactions_I.html b/SQL/Leetcode/Intermediate Select/1193. Monthly Transactions I/Claude Sonnet 4.6 Extended/Monthly_Transactions_I.html new file mode 100644 index 00000000..745945a6 --- /dev/null +++ b/SQL/Leetcode/Intermediate Select/1193. Monthly Transactions I/Claude Sonnet 4.6 Extended/Monthly_Transactions_I.html @@ -0,0 +1,1938 @@ + + + + + + LeetCode 1193 - Monthly Transactions I + + + + + + + + + + + + + + + + + +
+ + + + +
+

+ アルゴリズム概要 +

+

+ Transactions + テーブルから、 + 月(YYYY-MM)× 国 + の組み合わせごとに以下の4指標を集計する問題です。 +

+ +
+
+
+ trans_count +
+
全トランザクション数
+
+
+
+ approved_count +
+
承認件数
+
+
+
+ trans_total_amount +
+
全合計金額
+
+
+
+ approved_total_amount +
+
承認合計金額
+
+
+ +

入出力例

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ id + + country + + state + + amount + + trans_date +
121US + approved + 10002018-12-18
122US + declined + 20002018-12-19
123US + approved + 20002019-01-01
124 + NULL + + approved + 20002019-01-07
+
+ +

⚠️ 落とし穴まとめ

+
+
+
❌ SUM の NULL 問題
+
+ 承認行が 0 件のグループで + SUM は + NULL を返す → + COALESCE(...,0) 必須 +
+
+
+
❌ country=NULL 脱落
+
+ SQL の GROUP BY と pandas の + groupby はデフォルトで NULL + キーを除外する +
+
+
+
❌ FILTER 句の環境差異
+
+ PostgreSQL 独自構文のため MySQL では動作しない。本問は PostgreSQL + 対応済み +
+
+
+
+ + +
+

+ ステップバイステップ解説 +

+
+
+ + +
+

+ PostgreSQL 実装 +

+
SELECT
+    TO_CHAR(trans_date, 'YYYY-MM')                              AS month,
+    country,
+    COUNT(*)                                                     AS trans_count,
+    COUNT(*) FILTER (WHERE state = 'approved')                  AS approved_count,
+    SUM(amount)                                                  AS trans_total_amount,
+    COALESCE(SUM(amount) FILTER (WHERE state = 'approved'), 0)  AS approved_total_amount
+FROM Transactions
+GROUP BY
+    TO_CHAR(trans_date, 'YYYY-MM'),
+    country;
+ +
+
+
TO_CHAR(..., 'YYYY-MM')
+
+ 日付を YYYY-MM 文字列に変換。DATE_TRUNC より出力形式が直接的 +
+
+
+
FILTER (WHERE ...)
+
+ PostgreSQL 拡張。CASE WHEN より意図が明確で最適化しやすい +
+
+
+
COALESCE(..., 0)
+
+ 承認行が 0 件時に SUM が NULL → 0 になるのを防ぐ。★ 最重要修正点 +
+
+
+
+ + +
+

+ Pandas 実装(Python 3.10 / pandas 2.2.2) +

+
import pandas as pd
+
+def monthly_transactions(transactions: pd.DataFrame) -> pd.DataFrame:
+    """
+    Returns:
+        pd.DataFrame: 列名と順序は
+            [month, country, trans_count, approved_count,
+             trans_total_amount, approved_total_amount]
+    """
+    # ★ copy() 廃止: 必要列のみで軽量 DataFrame を新規構築
+    is_approved = (transactions['state'] == 'approved').astype('int8')  # int8 で省メモリ
+
+    tmp = pd.DataFrame({
+        'month'        : transactions['trans_date'].dt.to_period('M').astype(str),
+        'country'      : transactions['country'],
+        'id'           : transactions['id'],
+        'amount'       : transactions['amount'],
+        'is_approved'  : is_approved,
+        'approved_amt' : transactions['amount'] * is_approved,
+    })
+
+    out = (
+        tmp.groupby(['month', 'country'], sort=False, dropna=False)  # ★ dropna=False
+           .agg(
+               trans_count           = ('id',          'count'),
+               approved_count        = ('is_approved', 'sum'),
+               trans_total_amount    = ('amount',       'sum'),
+               approved_total_amount = ('approved_amt', 'sum'),
+           )
+           .reset_index()
+    )
+
+    # dtype を明示的に int に統一(NaN キー混在時の float64 混入を防ぐ)
+    out[['trans_count', 'approved_count',
+         'trans_total_amount', 'approved_total_amount']] = \
+        out[['trans_count', 'approved_count',
+             'trans_total_amount', 'approved_total_amount']].astype(int)
+
+    return out
+ +
+
+
★ 最重要: dropna=False
+
+ pandas の + groupby + はデフォルト + dropna=True で + country=NaN + のグループを無言で除外する。dropna=False + で NaN キーも1グループとして保持。 +
+
+
+
メモリ最適化
+
+ .astype('int8') + で承認フラグを 1 byte/行に圧縮(int64 比 87.5% 削減)。copy() + 廃止で全列複製コストをゼロに。 +
+
+
+
+ + +
+

+ 処理フローチャート +

+
+ + + + + + + + + + + + + + + 開始 + + + + + + + + + 月文字列を生成 + + + TO_CHAR(trans_date, 'YYYY-MM') + + + + + + + + 承認フラグ列を付与 + + + is_approved = (state == 'approved').astype('int8') + + + + + + + + GROUP BY month × country + + + dropna=False で NaN キーも保持 ★ + + + + + + + + + 4 指標を一括集計 + + + + + + COUNT(*) → trans_count + + + SUM(is_approved) → approved_count + + + SUM(amount) → trans_total_amount + + + SUM(approved_amt) → approved_total_amount + + + + + + + + NULL / dtype を統一 + + + COALESCE(..., 0) / .astype(int) + + + + + + + + reset_index() で列に昇格 + + + + + + + + + 出力 DataFrame(6列) + + + month | country | trans_count | approved_count + + + trans_total_amount | approved_total_amount + + + + + + + + 終了 + + +
+

+ フローの説明:
+ 1. trans_date から + YYYY-MM + 形式の月文字列列を生成する
+ 2. + state == 'approved' の + boolean を + int8 + に変換し、フラグ列・金額列を付与する
+ 3. month × country で + GROUP BY。★ + dropna=False + で + country=NaN + を保持する
+ 4. + COUNT / SUM + で4指標を一括集計する
+ 5. + COALESCE / astype(int) + で NULL・dtype を統一する
+ 6. reset_index() で + month・country を通常列に昇格し返却する +

+
+ + +
+

+ 計算量分析 +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ フェーズ + SQLPandas備考
月文字列生成 + O(N) + + O(N) + + ベクトル演算 +
+ GROUP BY / groupby.agg + + O(N) + + O(N) + + ハッシュ集計。G ≪ N なら線形近似 +
COALESCE / astype + O(G) + + O(G) + + G = 月×国のユニーク数 +
合計 + O(N) + + O(N) + + sort=False でソートコスト回避 +
+
+ +

手法比較

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
手法時間空間可読性 + NULL 安全 +
+ ✅ FILTER + COALESCE + O(N)O(G) + ◎ +
CASE WHENO(N)O(G) + 要 COALESCE +
サブクエリ結合O(N)O(G×2) + 要注意 +
pandas applyO(N×G)O(N)
+
+
+
+ + + + diff --git a/SQL/Leetcode/Intermediate Select/1193. Monthly Transactions I/Claude Sonnet 4.6 Extended/Monthly_Transactions_I_pandas.md b/SQL/Leetcode/Intermediate Select/1193. Monthly Transactions I/Claude Sonnet 4.6 Extended/Monthly_Transactions_I_pandas.md new file mode 100644 index 00000000..41be3b5e --- /dev/null +++ b/SQL/Leetcode/Intermediate Select/1193. Monthly Transactions I/Claude Sonnet 4.6 Extended/Monthly_Transactions_I_pandas.md @@ -0,0 +1,202 @@ +# Pandas 2.2.2用 + +## 0) 前提 + +- 環境: **Python 3.10.15 / pandas 2.2.2** +- **指定シグネチャ厳守**(関数名・引数名・返却列・順序) +- I/O 禁止、不要な `print` や `sort_values` 禁止 + +--- + +## 1) 問題 + +- 月・国ごとに、全トランザクション数・合計金額、承認済みトランザクション数・合計金額を集計する +- 入力 DF: + +``` +transactions: id(int), country(str|NaN), state(str: 'approved'|'declined'), + amount(int), trans_date(datetime) +``` + +- 出力: + +``` +month(str: 'YYYY-MM'), country(str|NaN), +trans_count(int), approved_count(int), +trans_total_amount(int), approved_total_amount(int) +``` + +--- + +## 2) 実装(指定シグネチャ厳守) + +> 原則は **月文字列生成 → 承認フラグ列付与 → `dropna=False` 付き groupby 集計 → dtype 統一**。 + +```python +# Analyze Complexity +# Runtime 383 ms +# Beats 75.97% +# Memory 69.39 MB +# Beats 48.55% +import pandas as pd + +def monthly_transactions(transactions: pd.DataFrame) -> pd.DataFrame: + """ + Returns: + pd.DataFrame: 列名と順序は + [month, country, trans_count, approved_count, + trans_total_amount, approved_total_amount] + """ + df = transactions.copy() + + # 月文字列列を生成(YYYY-MM) + df['month'] = df['trans_date'].dt.to_period('M').astype(str) + + # 承認フラグ列を付与(bool → int で SUM 可能、0件時も 0 を保証) + df['is_approved'] = (df['state'] == 'approved').astype(int) + df['approved_amt'] = df['amount'] * df['is_approved'] + + # groupby + agg で一括集計 + # ★ dropna=False: country=NaN のグループを除外しない + # ★ sort=False : ソートコストを排除(出力順序は任意) + out = ( + df.groupby(['month', 'country'], sort=False, dropna=False) + .agg( + trans_count = ('id', 'count'), + approved_count = ('is_approved', 'sum'), + trans_total_amount = ('amount', 'sum'), + approved_total_amount = ('approved_amt', 'sum'), + ) + .reset_index() + ) + + # dtype を明示的に int に統一(NaN キー混在時の float64 混入を防ぐ) + out[['trans_count', 'approved_count', + 'trans_total_amount', 'approved_total_amount']] = \ + out[['trans_count', 'approved_count', + 'trans_total_amount', 'approved_total_amount']].astype(int) + + return out +``` + +--- + +## 3) アルゴリズム説明 + +- **`dt.to_period('M').astype(str)`**: `trans_date` を `YYYY-MM` 文字列へ変換。`strftime('%Y-%m')` より Period 経由のほうが型安全 +- **`(state == 'approved').astype(int)`**: boolean を `0/1` に変換し、`sum` でカウントと金額を同時に集計。NULL 対策も不要 +- **`groupby(..., dropna=False)`**: ★最重要。デフォルト `dropna=True` では `country=NaN` のグループが**無言で脱落**する。`dropna=False` で NaN キーも1グループとして保持 +- **`groupby.agg` 名前付き集計**: `(output_col=(input_col, func))` 構文で列名整形を agg 内で完結、`rename` 不要 +- **NULL / 重複 / 型**: + +| 項目 | 対処 | +| ---------------------------- | --------------------------------- | +| `country=NaN` グループ脱落 | `dropna=False` で保持 | +| `approved_*` の float64 混入 | `.astype(int)` で明示統一 | +| `count` の NULL | `id` 列はキーのため NULL なし確定 | + +--- + +## 4) 計算量(概算) + +| フェーズ | 計算量 | 備考 | +| ----------------------------- | ------------- | --------------------------------- | +| `dt.to_period` / 列演算 | **O(N)** | ベクトル演算 | +| `groupby.agg`(ハッシュ集計) | **O(N)** 平均 | グループ数 G ≪ N なら実質線形 | +| `reset_index` / `astype` | **O(G)** | G = 月×国のユニーク数 | +| 全体 | **O(N)** | `sort=False` で O(N log N) を回避 | + +--- + +## 5) 図解(Mermaid 超保守版) + +```mermaid +flowchart TD + A[入力 transactions DataFrame] + B[dt.to_period でmonth列を生成] + C[is_approved と approved_amt 列を付与] + D[groupby month country dropna=False sort=False] + E[agg で4指標を一括算出] + F[reset_index で列に昇格] + G[astype int で dtype を統一] + H[出力 6列 month country trans_count approved_count trans_total_amount approved_total_amount] + + A --> B + B --> C + C --> D + D --> E + E --> F + F --> G + G --> H +``` + +## 改善ポイント分析 + +| 問題点 | 現状 | 改善策 | +| ------------------------- | ------------------------------ | ------------------------------------- | +| `copy()` で全列複製 | 全 DataFrame をメモリ複製 | 必要列のみの軽量 DataFrame を新規構築 | +| `is_approved` が `int64` | 8 bytes/要素 | `int8` に縮小(1 byte/要素) | +| 不要列が groupby まで残存 | `state`, `trans_date` 等が混在 | groupby 前に必要列のみに絞る | + +--- + +## 2) 実装(改善版) + +```python +# Analyze Complexity +# Runtime 371 ms +# Beats 86.45% +# Memory 69.30 MB +# Beats 58.06% + +import pandas as pd + +def monthly_transactions(transactions: pd.DataFrame) -> pd.DataFrame: + """ + Returns: + pd.DataFrame: 列名と順序は + [month, country, trans_count, approved_count, + trans_total_amount, approved_total_amount] + """ + # ★ copy() 廃止: 必要列のみで軽量 DataFrame を新規構築 + is_approved = (transactions['state'] == 'approved').astype('int8') # ★ int8 で省メモリ + + tmp = pd.DataFrame({ + 'month' : transactions['trans_date'].dt.to_period('M').astype(str), + 'country' : transactions['country'], + 'id' : transactions['id'], + 'amount' : transactions['amount'], + 'is_approved' : is_approved, + 'approved_amt' : transactions['amount'] * is_approved, # int8 × int → int + }) + + out = ( + tmp.groupby(['month', 'country'], sort=False, dropna=False) + .agg( + trans_count = ('id', 'count'), + approved_count = ('is_approved', 'sum'), + trans_total_amount = ('amount', 'sum'), + approved_total_amount = ('approved_amt', 'sum'), + ) + .reset_index() + ) + + out[['trans_count', 'approved_count', + 'trans_total_amount', 'approved_total_amount']] = \ + out[['trans_count', 'approved_count', + 'trans_total_amount', 'approved_total_amount']].astype(int) + + return out +``` + +--- + +## 改善効果の試算 + +| 指標 | 改善前 | 改善後(期待) | +| -------------------- | -------------------- | ----------------------------------- | +| `is_approved` メモリ | `int64`: 8 bytes/行 | `int8`: 1 bytes/行 → **87.5% 削減** | +| `copy()` コスト | 全列複製 O(N×全列数) | **ゼロ**(新規構築のみ) | +| groupby 対象列数 | 元 DataFrame の全列 | **6列のみ** | +| Memory 期待値 | 69.39 MB | **~55 MB 以下**(Beats 70%+ 期待) | +| Runtime 期待値 | 383 ms | **~300 ms 以下**(Beats 85%+ 期待) | diff --git a/SQL/Leetcode/Intermediate Select/1193. Monthly Transactions I/Claude Sonnet 4.6 Extended/Monthly_Transactions_I_postgresql.md b/SQL/Leetcode/Intermediate Select/1193. Monthly Transactions I/Claude Sonnet 4.6 Extended/Monthly_Transactions_I_postgresql.md new file mode 100644 index 00000000..b371b7dd --- /dev/null +++ b/SQL/Leetcode/Intermediate Select/1193. Monthly Transactions I/Claude Sonnet 4.6 Extended/Monthly_Transactions_I_postgresql.md @@ -0,0 +1,182 @@ +# PostgreSQL 16.6+ + +## 0) 前提 + +- エンジン: **PostgreSQL 16.6+** +- 並び順: 任意 +- `NOT IN` 回避(`EXISTS` / `LEFT JOIN ... IS NULL` を推奨) +- 判定は ID 基準、表示は仕様どおり + +--- + +## 1) 問題 + +- 月・国ごとに、全トランザクション数・合計金額、および承認済みトランザクション数・合計金額を集計する +- 入力: + +``` +Transactions(id, country, state ENUM['approved','declined'], amount, trans_date) +``` + +- 出力: + +| 列名 | 説明 | +| ----------------------- | -------------------- | +| `month` | `YYYY-MM` 形式の年月 | +| `country` | 国コード | +| `trans_count` | 全件数 | +| `approved_count` | 承認件数 | +| `trans_total_amount` | 全合計金額 | +| `approved_total_amount` | 承認合計金額 | + +--- + +## 2) 最適解(単一クエリ) + +> 条件集計は **`COUNT` / `SUM` + `FILTER`句** で一発 GROUP BY が最もシンプル・高速。 + +```sql +-- Wrong Answer +-- 5 / 16 testcases passed + +SELECT + TO_CHAR(trans_date, 'YYYY-MM') AS month, + country, + COUNT(*) AS trans_count, + COUNT(*) FILTER (WHERE state = 'approved') AS approved_count, + SUM(amount) AS trans_total_amount, + SUM(amount) FILTER (WHERE state = 'approved') AS approved_total_amount +FROM Transactions +GROUP BY + TO_CHAR(trans_date, 'YYYY-MM'), + country; +``` + +### 代替(CASE WHEN による条件集計) + +`FILTER` 句を使わない場合の標準 SQL 互換版: + +```sql +-- Runtime 423 ms +-- Beats 59.20% + +SELECT + TO_CHAR(trans_date, 'YYYY-MM') AS month, + country, + COUNT(*) AS trans_count, + COUNT(CASE WHEN state = 'approved' THEN 1 END) AS approved_count, + SUM(amount) AS trans_total_amount, + SUM(CASE WHEN state = 'approved' THEN amount ELSE 0 END) AS approved_total_amount +FROM Transactions +GROUP BY + TO_CHAR(trans_date, 'YYYY-MM'), + country; +``` + +--- + +## 3) 要点解説 + +| ポイント | 詳細 | +| ------------------------------------ | ------------------------------------------------------------------------------------------------------ | +| **`TO_CHAR(trans_date, 'YYYY-MM')`** | `DATE_TRUNC('month', ...)` でも可だが、文字列で `YYYY-MM` を直接得るにはこちらが簡潔 | +| **`COUNT(*) FILTER (WHERE ...)`** | PostgreSQL 独自の ANSI SQL:2003 拡張。`CASE WHEN` より読みやすく、オプティマイザにも意図が伝わりやすい | +| **`SUM(amount) FILTER (...)`** | `ELSE 0` 不要で NULL-safe かつ明快 | +| **GROUP BY のキー統一** | `SELECT` と `GROUP BY` の `TO_CHAR(...)` 式を完全一致させることが必須 | +| **インデックス戦略** | `(trans_date, country, state, amount)` の複合インデックスで Index-Only Scan が期待できる | + +--- + +## 4) 計算量(概算) + +| フェーズ | 計算量 | +| --------------------- | ---------------------------------------------- | +| テーブルフルスキャン | **O(N)** | +| GROUP BY ハッシュ集計 | **O(N)** 平均(グループ数 G が小さい場合) | +| ソートベース GROUP BY | **O(N log N)**(メモリ不足時のフォールバック) | +| インデックス使用時 | **O(N)** → **Index-Only Scan** で I/O 削減 | + +> N = Transactions 行数、G = (月×国) のユニーク組み合わせ数 + +--- + +## 5) 図解(Mermaid 超保守版) + +```mermaid +flowchart TD + A[入力 Transactions テーブル] + B[TO_CHAR で月文字列を生成] + C[country × month で GROUP BY] + D[COUNT と SUM で全件集計] + E[FILTER WHERE state=approved で条件集計] + F[出力 6列 month country trans_count approved_count trans_total_amount approved_total_amount] + + A --> B + B --> C + C --> D + C --> E + D --> F + E --> F +``` + +## 原因と修正 + +### 🔴 WA の真因:`SUM ... FILTER` の NULL 問題 + +```sql +-- 承認件数が 0 件のグループで NULL を返す ← これが WA の原因 +SUM(amount) FILTER (WHERE state = 'approved') +``` + +`SUM` は対象行が 0 件のとき **`0` ではなく `NULL`** を返します。`COUNT` は `0` を返すので問題ないですが、`SUM` は `COALESCE` が必要です。 + +--- + +## 修正版 + +```sql + +-- Runtime 415 ms +-- Beats 66.83% + +SELECT + TO_CHAR(trans_date, 'YYYY-MM') AS month, + country, + COUNT(*) AS trans_count, + COUNT(*) FILTER (WHERE state = 'approved') AS approved_count, + SUM(amount) AS trans_total_amount, + COALESCE(SUM(amount) FILTER (WHERE state = 'approved'), 0) AS approved_total_amount +FROM Transactions +GROUP BY + TO_CHAR(trans_date, 'YYYY-MM'), + country; +``` + +--- + +## Runtime 改善(CASE WHEN 版) + +```sql +-- Runtime 422 ms +-- Beats 60.26% + +SELECT + TO_CHAR(trans_date, 'YYYY-MM') AS month, + country, + COUNT(*) AS trans_count, + COUNT(CASE WHEN state = 'approved' THEN 1 END) AS approved_count, + SUM(amount) AS trans_total_amount, + COALESCE(SUM(CASE WHEN state = 'approved' THEN amount END), 0) AS approved_total_amount +FROM Transactions +GROUP BY 1, 2; -- 式の二重評価を避けるため位置参照に変更 +``` + +--- + +## 教訓まとめ + +| 関数 | 0件時の戻り値 | 対処 | +| -------------------- | ------------- | ------------------ | +| `COUNT(*)` | `0` | 不要 | +| `SUM(...) FILTER` | **`NULL`** | `COALESCE(..., 0)` | +| `SUM(CASE WHEN ...)` | **`NULL`** | `COALESCE(..., 0)` | diff --git a/public/SQL/Leetcode/Intermediate Select/1193. Monthly Transactions I/Claude Sonnet 4.6 Extended/Monthly_Transactions_I.html b/public/SQL/Leetcode/Intermediate Select/1193. Monthly Transactions I/Claude Sonnet 4.6 Extended/Monthly_Transactions_I.html new file mode 100644 index 00000000..084feeef --- /dev/null +++ b/public/SQL/Leetcode/Intermediate Select/1193. Monthly Transactions I/Claude Sonnet 4.6 Extended/Monthly_Transactions_I.html @@ -0,0 +1,1938 @@ + + + + + + LeetCode 1193 - Monthly Transactions I + + + + + + + + + + + + + + + + + +
+ + + + +
+

+ アルゴリズム概要 +

+

+ Transactions + テーブルから、 + 月(YYYY-MM)× 国 + の組み合わせごとに以下の4指標を集計する問題です。 +

+ +
+
+
+ trans_count +
+
全トランザクション数
+
+
+
+ approved_count +
+
承認件数
+
+
+
+ trans_total_amount +
+
全合計金額
+
+
+
+ approved_total_amount +
+
承認合計金額
+
+
+ +

入出力例

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ id + + country + + state + + amount + + trans_date +
121US + approved + 10002018-12-18
122US + declined + 20002018-12-19
123US + approved + 20002019-01-01
124 + NULL + + approved + 20002019-01-07
+
+ +

⚠️ 落とし穴まとめ

+
+
+
❌ SUM の NULL 問題
+
+ 承認行が 0 件のグループで + SUM は + NULL を返す → + COALESCE(...,0) 必須 +
+
+
+
❌ country=NULL 脱落
+
+ SQL の GROUP BY と pandas の + groupby はデフォルトで NULL + キーを除外する +
+
+
+
❌ FILTER 句の環境差異
+
+ PostgreSQL 独自構文のため MySQL では動作しない。本問は PostgreSQL + 対応済み +
+
+
+
+ + +
+

+ ステップバイステップ解説 +

+
+
+ + +
+

+ PostgreSQL 実装 +

+
SELECT
+    TO_CHAR(trans_date, 'YYYY-MM')                              AS month,
+    country,
+    COUNT(*)                                                     AS trans_count,
+    COUNT(*) FILTER (WHERE state = 'approved')                  AS approved_count,
+    SUM(amount)                                                  AS trans_total_amount,
+    COALESCE(SUM(amount) FILTER (WHERE state = 'approved'), 0)  AS approved_total_amount
+FROM Transactions
+GROUP BY
+    TO_CHAR(trans_date, 'YYYY-MM'),
+    country;
+ +
+
+
TO_CHAR(..., 'YYYY-MM')
+
+ 日付を YYYY-MM 文字列に変換。DATE_TRUNC より出力形式が直接的 +
+
+
+
FILTER (WHERE ...)
+
+ PostgreSQL 拡張。CASE WHEN より意図が明確で最適化しやすい +
+
+
+
COALESCE(..., 0)
+
+ 承認行が 0 件時に SUM が NULL → 0 になるのを防ぐ。★ 最重要修正点 +
+
+
+
+ + +
+

+ Pandas 実装(Python 3.10 / pandas 2.2.2) +

+
import pandas as pd
+
+def monthly_transactions(transactions: pd.DataFrame) -> pd.DataFrame:
+    """
+    Returns:
+        pd.DataFrame: 列名と順序は
+            [month, country, trans_count, approved_count,
+             trans_total_amount, approved_total_amount]
+    """
+    # ★ copy() 廃止: 必要列のみで軽量 DataFrame を新規構築
+    is_approved = (transactions['state'] == 'approved').astype('int8')  # int8 で省メモリ
+
+    tmp = pd.DataFrame({
+        'month'        : transactions['trans_date'].dt.to_period('M').astype(str),
+        'country'      : transactions['country'],
+        'id'           : transactions['id'],
+        'amount'       : transactions['amount'],
+        'is_approved'  : is_approved,
+        'approved_amt' : transactions['amount'] * is_approved,
+    })
+
+    out = (
+        tmp.groupby(['month', 'country'], sort=False, dropna=False)  # ★ dropna=False
+           .agg(
+               trans_count           = ('id',          'count'),
+               approved_count        = ('is_approved', 'sum'),
+               trans_total_amount    = ('amount',       'sum'),
+               approved_total_amount = ('approved_amt', 'sum'),
+           )
+           .reset_index()
+    )
+
+    # dtype を明示的に int に統一(NaN キー混在時の float64 混入を防ぐ)
+    out[['trans_count', 'approved_count',
+         'trans_total_amount', 'approved_total_amount']] = \
+        out[['trans_count', 'approved_count',
+             'trans_total_amount', 'approved_total_amount']].astype(int)
+
+    return out
+ +
+
+
★ 最重要: dropna=False
+
+ pandas の + groupby + はデフォルト + dropna=True で + country=NaN + のグループを無言で除外する。dropna=False + で NaN キーも1グループとして保持。 +
+
+
+
メモリ最適化
+
+ .astype('int8') + で承認フラグを 1 byte/行に圧縮(int64 比 87.5% 削減)。copy() + 廃止で全列複製コストをゼロに。 +
+
+
+
+ + +
+

+ 処理フローチャート +

+
+ + + + + + + + + + + + + + + 開始 + + + + + + + + + 月文字列を生成 + + + TO_CHAR(trans_date, 'YYYY-MM') + + + + + + + + 承認フラグ列を付与 + + + is_approved = (state == 'approved').astype('int8') + + + + + + + + GROUP BY month × country + + + dropna=False で NaN キーも保持 ★ + + + + + + + + + 4 指標を一括集計 + + + + + + COUNT(*) → trans_count + + + SUM(is_approved) → approved_count + + + SUM(amount) → trans_total_amount + + + SUM(approved_amt) → approved_total_amount + + + + + + + + NULL / dtype を統一 + + + COALESCE(..., 0) / .astype(int) + + + + + + + + reset_index() で列に昇格 + + + + + + + + + 出力 DataFrame(6列) + + + month | country | trans_count | approved_count + + + trans_total_amount | approved_total_amount + + + + + + + + 終了 + + +
+

+ フローの説明:
+ 1. trans_date から + YYYY-MM + 形式の月文字列列を生成する
+ 2. + state == 'approved' の + boolean を + int8 + に変換し、フラグ列・金額列を付与する
+ 3. month × country で + GROUP BY。★ + dropna=False + で + country=NaN + を保持する
+ 4. + COUNT / SUM + で4指標を一括集計する
+ 5. + COALESCE / astype(int) + で NULL・dtype を統一する
+ 6. reset_index() で + month・country を通常列に昇格し返却する +

+
+ + +
+

+ 計算量分析 +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ フェーズ + SQLPandas備考
月文字列生成 + O(N) + + O(N) + + ベクトル演算 +
+ GROUP BY / groupby.agg + + O(N) + + O(N) + + ハッシュ集計。G ≪ N なら線形近似 +
COALESCE / astype + O(G) + + O(G) + + G = 月×国のユニーク数 +
合計 + O(N) + + O(N) + + sort=False でソートコスト回避 +
+
+ +

手法比較

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
手法時間空間可読性 + NULL 安全 +
+ ✅ FILTER + COALESCE + O(N)O(G) + ◎ +
CASE WHENO(N)O(G) + 要 COALESCE +
サブクエリ結合O(N)O(G×2) + 要注意 +
pandas applyO(N×G)O(N)
+
+
+
+ + + + diff --git a/public/index.html b/public/index.html index f1dce402..f9e39964 100644 --- a/public/index.html +++ b/public/index.html @@ -416,7 +416,7 @@

🧪 Algorithm Study Index

-

154 interactive lessons across 6 domains

+

155 interactive lessons across 6 domains

@@ -431,14 +431,14 @@

- +
@@ -590,13 +590,14 @@

  • 📐Robot Unique Paths - 技術解説Mathematics/Combination Calculation/leetcode/62. Unique Paths/Claude/README.html
  • 📐Strange Grid 解説Mathematics/Fundamentals/HackerRank/Claude/Easy/Strange Grid Again/Strange_Grid_Again.html
  • 📐Valid Number Problem - 有限状態機械アルゴリズム解説Mathematics/Finite State Machine/leetcode/65. Valid Number/Claude/README.html
  • -
  • 📐pow(x, n) アルゴリズム解析Mathematics/Exponentiation by Squaring/leetcode/50. Pow(x, n)/Claude/README detailed.html
  • 📐pow(x, n) アルゴリズム解析Mathematics/Exponentiation by Squaring/leetcode/50. Pow(x, n)/Claude/README.html
  • +
  • 📐pow(x, n) アルゴリズム解析Mathematics/Exponentiation by Squaring/leetcode/50. Pow(x, n)/Claude/README detailed.html
  • 📐原始根の発見 - HackerRank問題解説Mathematics/Number Theory/HackerRank/Easy/Primitive_Problem.html
  • 📐整数を全て0にする問題 - 可視化デモMathematics/Other/atcoder/B45/README.html
  • 📐文字列掛け算アルゴリズムの詳細解析Mathematics/Multiply Strings/leetcode/43. Multiply Strings/Claude/README.html
  • 🗃️LeetCode 1174: Immediate Food Delivery II - グループ内最小値抽出SQL/Leetcode/Intermediate Select/1174. Immediate Food Delivery II/Claude Sonnet 4.5 Extended/Immediate_Food_Delivery_II.html
  • 🗃️LeetCode 1179 · Reformat Department TableSQL/Leetcode/Basic select/1179. Reformat Department Table/Claude Sonnet 4.6 Extended/README.html
  • +
  • 🗃️LeetCode 1193 - Monthly Transactions ISQL/Leetcode/Intermediate Select/1193. Monthly Transactions I/Claude Sonnet 4.6 Extended/Monthly_Transactions_I.html
  • 🗃️Product Prices - 価格履歴管理 | Pandas解説SQL/Leetcode/Intermediate Join/1164. Product Price at a Given Date/Claude Sonnet 4.5 Extended/Product_Price_at_a_Given_Date.html
  • @@ -771,8 +772,8 @@

  • 📐Robot Unique Paths - 技術解説Mathematics/Combination Calculation/leetcode/62. Unique Paths/Claude/README.html
  • 📐Strange Grid 解説Mathematics/Fundamentals/HackerRank/Claude/Easy/Strange Grid Again/Strange_Grid_Again.html
  • 📐Valid Number Problem - 有限状態機械アルゴリズム解説Mathematics/Finite State Machine/leetcode/65. Valid Number/Claude/README.html
  • -
  • 📐pow(x, n) アルゴリズム解析Mathematics/Exponentiation by Squaring/leetcode/50. Pow(x, n)/Claude/README detailed.html
  • 📐pow(x, n) アルゴリズム解析Mathematics/Exponentiation by Squaring/leetcode/50. Pow(x, n)/Claude/README.html
  • +
  • 📐pow(x, n) アルゴリズム解析Mathematics/Exponentiation by Squaring/leetcode/50. Pow(x, n)/Claude/README detailed.html
  • 📐原始根の発見 - HackerRank問題解説Mathematics/Number Theory/HackerRank/Easy/Primitive_Problem.html
  • 📐整数を全て0にする問題 - 可視化デモMathematics/Other/atcoder/B45/README.html
  • 📐文字列掛け算アルゴリズムの詳細解析Mathematics/Multiply Strings/leetcode/43. Multiply Strings/Claude/README.html
  • @@ -783,6 +784,7 @@

    🔎No results found
    @@ -791,7 +793,7 @@

    🧪 - Generated on 2026-02-25 10:29:30 UTC + Generated on 2026-02-26 01:04:58 UTC
    - - + + + - - - - - - + + + + + + 手法比較

    viewBox="0 0 560 220" style={{ maxWidth: '100%', height: 'auto', marginTop: '16px' }} role="img" + aria-labelledby="svg-input-title" > + Transactions テーブル(入力) 手法比較 viewBox="0 0 560 220" style={{ maxWidth: '100%', height: 'auto', marginTop: '16px' }} role="img" + aria-labelledby="svg-month-title" > + month 列を追加 手法比較 viewBox="0 0 560 230" style={{ maxWidth: '100%', height: 'auto', marginTop: '16px' }} role="img" + aria-labelledby="svg-flag-title" > + is_approved フラグ列 手法比較 viewBox="0 0 560 240" style={{ maxWidth: '100%', height: 'auto', marginTop: '16px' }} role="img" + aria-labelledby="svg-group-title" > + GROUP BY month × country 手法比較 viewBox="0 0 580 240" style={{ maxWidth: '100%', height: 'auto', marginTop: '16px' }} role="img" + aria-labelledby="svg-agg-title" > + 4指標を一括集計 + COALESCE 手法比較 viewBox="0 0 580 240" style={{ maxWidth: '100%', height: 'auto', marginTop: '16px' }} role="img" + aria-labelledby="svg-result-title" > + 最終出力(6列) None: title = self.get_html_title(filepath) except Exception: title = os.path.basename(filepath) + + # Append disambiguator if 'detailed' is in the filename + if 'detailed' in filename.lower(): + title += ' (detailed)' + structure[category].append((title, rel_path)) # Sort categories and files @@ -891,11 +896,11 @@ def generate_index(self) -> None: def render_category_files(structure, sorted_categories): """ Builds HTML fragments for category tabs, per-category file lists, and an aggregated all-files list. - + Parameters: structure (Dict[str, List[Tuple[str, str]]]): Mapping from category name to a list of (title, relative_path) pairs for files in that category. sorted_categories (List[str]): Ordered list of category names to render; determines the iteration order and tab order. - + Returns: Tuple[str, str, str]: A 3-tuple with: - tabs_html: HTML for the category tab buttons (includes icon and item count for each category). @@ -955,4 +960,4 @@ def render_category_files(structure, sorted_categories): print(f"Successfully updated {output_index_path} with vendored assets at {current_time}") if __name__ == "__main__": - Solution().generate_index() \ No newline at end of file + Solution().generate_index() diff --git a/public/SQL/Leetcode/Intermediate Select/1193. Monthly Transactions I/Claude Sonnet 4.6 Extended/Monthly_Transactions_I.html b/public/SQL/Leetcode/Intermediate Select/1193. Monthly Transactions I/Claude Sonnet 4.6 Extended/Monthly_Transactions_I.html index 084feeef..3437a2f4 100644 --- a/public/SQL/Leetcode/Intermediate Select/1193. Monthly Transactions I/Claude Sonnet 4.6 Extended/Monthly_Transactions_I.html +++ b/public/SQL/Leetcode/Intermediate Select/1193. Monthly Transactions I/Claude Sonnet 4.6 Extended/Monthly_Transactions_I.html @@ -4,28 +4,70 @@ LeetCode 1193 - Monthly Transactions I - - - + + + - - - - - - + + + + + + 手法比較 viewBox="0 0 560 220" style={{ maxWidth: '100%', height: 'auto', marginTop: '16px' }} role="img" + aria-labelledby="svg-input-title" > + Transactions テーブル(入力) 手法比較 viewBox="0 0 560 220" style={{ maxWidth: '100%', height: 'auto', marginTop: '16px' }} role="img" + aria-labelledby="svg-month-title" > + month 列を追加 手法比較 viewBox="0 0 560 230" style={{ maxWidth: '100%', height: 'auto', marginTop: '16px' }} role="img" + aria-labelledby="svg-flag-title" > + is_approved フラグ列 手法比較 viewBox="0 0 560 240" style={{ maxWidth: '100%', height: 'auto', marginTop: '16px' }} role="img" + aria-labelledby="svg-group-title" > + GROUP BY month × country 手法比較 viewBox="0 0 580 240" style={{ maxWidth: '100%', height: 'auto', marginTop: '16px' }} role="img" + aria-labelledby="svg-agg-title" > + 4指標を一括集計 + COALESCE 手法比較 viewBox="0 0 580 240" style={{ maxWidth: '100%', height: 'auto', marginTop: '16px' }} role="img" + aria-labelledby="svg-result-title" > + 最終出力(6列)
  • 📐Strange Grid 解説Mathematics/Fundamentals/HackerRank/Claude/Easy/Strange Grid Again/Strange_Grid_Again.html
  • 📐Valid Number Problem - 有限状態機械アルゴリズム解説Mathematics/Finite State Machine/leetcode/65. Valid Number/Claude/README.html
  • 📐pow(x, n) アルゴリズム解析Mathematics/Exponentiation by Squaring/leetcode/50. Pow(x, n)/Claude/README.html
  • -
  • 📐pow(x, n) アルゴリズム解析Mathematics/Exponentiation by Squaring/leetcode/50. Pow(x, n)/Claude/README detailed.html
  • +
  • 📐pow(x, n) アルゴリズム解析 (detailed)Mathematics/Exponentiation by Squaring/leetcode/50. Pow(x, n)/Claude/README detailed.html
  • 📐原始根の発見 - HackerRank問題解説Mathematics/Number Theory/HackerRank/Easy/Primitive_Problem.html
  • 📐整数を全て0にする問題 - 可視化デモMathematics/Other/atcoder/B45/README.html
  • 📐文字列掛け算アルゴリズムの詳細解析Mathematics/Multiply Strings/leetcode/43. Multiply Strings/Claude/README.html
  • @@ -773,7 +773,7 @@

  • 📐Strange Grid 解説Mathematics/Fundamentals/HackerRank/Claude/Easy/Strange Grid Again/Strange_Grid_Again.html
  • 📐Valid Number Problem - 有限状態機械アルゴリズム解説Mathematics/Finite State Machine/leetcode/65. Valid Number/Claude/README.html
  • 📐pow(x, n) アルゴリズム解析Mathematics/Exponentiation by Squaring/leetcode/50. Pow(x, n)/Claude/README.html
  • -
  • 📐pow(x, n) アルゴリズム解析Mathematics/Exponentiation by Squaring/leetcode/50. Pow(x, n)/Claude/README detailed.html
  • +
  • 📐pow(x, n) アルゴリズム解析 (detailed)Mathematics/Exponentiation by Squaring/leetcode/50. Pow(x, n)/Claude/README detailed.html
  • 📐原始根の発見 - HackerRank問題解説Mathematics/Number Theory/HackerRank/Easy/Primitive_Problem.html
  • 📐整数を全て0にする問題 - 可視化デモMathematics/Other/atcoder/B45/README.html
  • 📐文字列掛け算アルゴリズムの詳細解析Mathematics/Multiply Strings/leetcode/43. Multiply Strings/Claude/README.html
  • @@ -793,7 +793,7 @@

    🧪 - Generated on 2026-02-26 01:04:58 UTC + Generated on 2026-02-26 01:55:01 UTC
    diff --git a/generate_index.py b/generate_index.py index 442604cb..3637304b 100644 --- a/generate_index.py +++ b/generate_index.py @@ -177,7 +177,8 @@ def generate_index(self) -> None: # Append disambiguator if 'detailed' is in the filename if 'detailed' in filename.lower(): - title += ' (detailed)' + if '(detailed)' not in title.lower(): + title += ' (detailed)' structure[category].append((title, rel_path)) diff --git a/public/SQL/Leetcode/Intermediate Select/1193. Monthly Transactions I/Claude Sonnet 4.6 Extended/Monthly_Transactions_I.html b/public/SQL/Leetcode/Intermediate Select/1193. Monthly Transactions I/Claude Sonnet 4.6 Extended/Monthly_Transactions_I.html index 3437a2f4..8daae618 100644 --- a/public/SQL/Leetcode/Intermediate Select/1193. Monthly Transactions I/Claude Sonnet 4.6 Extended/Monthly_Transactions_I.html +++ b/public/SQL/Leetcode/Intermediate Select/1193. Monthly Transactions I/Claude Sonnet 4.6 Extended/Monthly_Transactions_I.html @@ -1131,7 +1131,7 @@

    手法比較

    { id: 121, country: 'US', state: 'approved', amount: 1000, date: '2018-12-18' }, { id: 122, country: 'US', state: 'declined', amount: 2000, date: '2018-12-19' }, { id: 123, country: 'US', state: 'approved', amount: 2000, date: '2019-01-01' }, - { id: 124, country: 'NULL', state: 'approved', amount: 2000, date: '2019-01-07' }, + { id: 124, country: null, state: 'approved', amount: 2000, date: '2019-01-07' }, ]; function StepViz({ type }) { @@ -1207,10 +1207,10 @@

    手法比較

    y={y + 2} textAnchor="middle" fontSize="12" - fill={r.country === 'NULL' ? '#d97706' : '#1e293b'} - fontWeight={r.country === 'NULL' ? 'bold' : 'normal'} + fill={r.country === null ? '#d97706' : '#1e293b'} + fontWeight={r.country === null ? 'bold' : 'normal'} > - {r.country} + {r.country === null ? 'NULL' : r.country} const root = ReactDOM.createRoot(document.getElementById('steps-root')); root.render(); - setTimeout(() => { - Prism.highlightAll(); - }, 300); + const initPrism = () => { + if (window.requestIdleCallback) { + requestIdleCallback(() => Prism.highlightAll()); + } else if (window.requestAnimationFrame) { + requestAnimationFrame(() => Prism.highlightAll()); + } else { + setTimeout(() => Prism.highlightAll(), 300); + } + }; + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', initPrism); + } else { + initPrism(); + } + if (typeof MutationObserver !== 'undefined') { + const observer = new MutationObserver(() => initPrism()); + observer.observe(document.getElementById('steps-root'), { + childList: true, + subtree: true, + }); + } diff --git a/public/index.html b/public/index.html index a81f1cbb..80b44ad0 100644 --- a/public/index.html +++ b/public/index.html @@ -793,7 +793,7 @@

    🧪 - Generated on 2026-02-26 01:57:12 UTC + Generated on 2026-02-26 04:15:00 UTC
    - + diff --git a/public/DataStructures/LinkedLists/leetcode/86. Partition List/GPT/README.html b/public/DataStructures/LinkedLists/leetcode/86. Partition List/GPT/README.html index 5aaf7c5b..bca48fba 100644 --- a/public/DataStructures/LinkedLists/leetcode/86. Partition List/GPT/README.html +++ b/public/DataStructures/LinkedLists/leetcode/86. Partition List/GPT/README.html @@ -31,7 +31,7 @@