Skip to content

Commit a862c49

Browse files
committed
Add 1193. Monthly Transactions I and update notebook kernelspec
1 parent d6f6fc9 commit a862c49

6 files changed

Lines changed: 4275 additions & 7 deletions

File tree

Mathematics/Fundamentals/HackerRank/Claude/Easy/Sherlock and Divisors/Sherlock_and_Divisors.ipynb

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,14 @@
6363
}
6464
],
6565
"metadata": {
66+
"kernelspec": {
67+
"display_name": ".venv",
68+
"language": "python",
69+
"name": "python3"
70+
},
6671
"language_info": {
67-
"name": "python"
72+
"name": "python",
73+
"version": "3.12.4"
6874
}
6975
},
7076
"nbformat": 4,

SQL/Leetcode/Intermediate Select/1193. Monthly Transactions I/Claude Sonnet 4.6 Extended/Monthly_Transactions_I.html

Lines changed: 1938 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
# Pandas 2.2.2用
2+
3+
## 0) 前提
4+
5+
- 環境: **Python 3.10.15 / pandas 2.2.2**
6+
- **指定シグネチャ厳守**(関数名・引数名・返却列・順序)
7+
- I/O 禁止、不要な `print``sort_values` 禁止
8+
9+
---
10+
11+
## 1) 問題
12+
13+
- 月・国ごとに、全トランザクション数・合計金額、承認済みトランザクション数・合計金額を集計する
14+
- 入力 DF:
15+
16+
```
17+
transactions: id(int), country(str|NaN), state(str: 'approved'|'declined'),
18+
amount(int), trans_date(datetime)
19+
```
20+
21+
- 出力:
22+
23+
```
24+
month(str: 'YYYY-MM'), country(str|NaN),
25+
trans_count(int), approved_count(int),
26+
trans_total_amount(int), approved_total_amount(int)
27+
```
28+
29+
---
30+
31+
## 2) 実装(指定シグネチャ厳守)
32+
33+
> 原則は **月文字列生成 → 承認フラグ列付与 → `dropna=False` 付き groupby 集計 → dtype 統一**
34+
35+
```python
36+
# Analyze Complexity
37+
# Runtime 383 ms
38+
# Beats 75.97%
39+
# Memory 69.39 MB
40+
# Beats 48.55%
41+
import pandas as pd
42+
43+
def monthly_transactions(transactions: pd.DataFrame) -> pd.DataFrame:
44+
"""
45+
Returns:
46+
pd.DataFrame: 列名と順序は
47+
[month, country, trans_count, approved_count,
48+
trans_total_amount, approved_total_amount]
49+
"""
50+
df = transactions.copy()
51+
52+
# 月文字列列を生成(YYYY-MM)
53+
df['month'] = df['trans_date'].dt.to_period('M').astype(str)
54+
55+
# 承認フラグ列を付与(bool → int で SUM 可能、0件時も 0 を保証)
56+
df['is_approved'] = (df['state'] == 'approved').astype(int)
57+
df['approved_amt'] = df['amount'] * df['is_approved']
58+
59+
# groupby + agg で一括集計
60+
# ★ dropna=False: country=NaN のグループを除外しない
61+
# ★ sort=False : ソートコストを排除(出力順序は任意)
62+
out = (
63+
df.groupby(['month', 'country'], sort=False, dropna=False)
64+
.agg(
65+
trans_count = ('id', 'count'),
66+
approved_count = ('is_approved', 'sum'),
67+
trans_total_amount = ('amount', 'sum'),
68+
approved_total_amount = ('approved_amt', 'sum'),
69+
)
70+
.reset_index()
71+
)
72+
73+
# dtype を明示的に int に統一(NaN キー混在時の float64 混入を防ぐ)
74+
out[['trans_count', 'approved_count',
75+
'trans_total_amount', 'approved_total_amount']] = \
76+
out[['trans_count', 'approved_count',
77+
'trans_total_amount', 'approved_total_amount']].astype(int)
78+
79+
return out
80+
```
81+
82+
---
83+
84+
## 3) アルゴリズム説明
85+
86+
- **`dt.to_period('M').astype(str)`**: `trans_date``YYYY-MM` 文字列へ変換。`strftime('%Y-%m')` より Period 経由のほうが型安全
87+
- **`(state == 'approved').astype(int)`**: boolean を `0/1` に変換し、`sum` でカウントと金額を同時に集計。NULL 対策も不要
88+
- **`groupby(..., dropna=False)`**: ★最重要。デフォルト `dropna=True` では `country=NaN` のグループが**無言で脱落**する。`dropna=False` で NaN キーも1グループとして保持
89+
- **`groupby.agg` 名前付き集計**: `(output_col=(input_col, func))` 構文で列名整形を agg 内で完結、`rename` 不要
90+
- **NULL / 重複 / 型**:
91+
92+
| 項目 | 対処 |
93+
| ---------------------------- | --------------------------------- |
94+
| `country=NaN` グループ脱落 | `dropna=False` で保持 |
95+
| `approved_*` の float64 混入 | `.astype(int)` で明示統一 |
96+
| `count` の NULL | `id` 列はキーのため NULL なし確定 |
97+
98+
---
99+
100+
## 4) 計算量(概算)
101+
102+
| フェーズ | 計算量 | 備考 |
103+
| ----------------------------- | ------------- | --------------------------------- |
104+
| `dt.to_period` / 列演算 | **O(N)** | ベクトル演算 |
105+
| `groupby.agg`(ハッシュ集計) | **O(N)** 平均 | グループ数 G ≪ N なら実質線形 |
106+
| `reset_index` / `astype` | **O(G)** | G = 月×国のユニーク数 |
107+
| 全体 | **O(N)** | `sort=False` で O(N log N) を回避 |
108+
109+
---
110+
111+
## 5) 図解(Mermaid 超保守版)
112+
113+
```mermaid
114+
flowchart TD
115+
A[入力 transactions DataFrame]
116+
B[dt.to_period でmonth列を生成]
117+
C[is_approved と approved_amt 列を付与]
118+
D[groupby month country dropna=False sort=False]
119+
E[agg で4指標を一括算出]
120+
F[reset_index で列に昇格]
121+
G[astype int で dtype を統一]
122+
H[出力 6列 month country trans_count approved_count trans_total_amount approved_total_amount]
123+
124+
A --> B
125+
B --> C
126+
C --> D
127+
D --> E
128+
E --> F
129+
F --> G
130+
G --> H
131+
```
132+
133+
## 改善ポイント分析
134+
135+
| 問題点 | 現状 | 改善策 |
136+
| ------------------------- | ------------------------------ | ------------------------------------- |
137+
| `copy()` で全列複製 | 全 DataFrame をメモリ複製 | 必要列のみの軽量 DataFrame を新規構築 |
138+
| `is_approved``int64` | 8 bytes/要素 | `int8` に縮小(1 byte/要素) |
139+
| 不要列が groupby まで残存 | `state`, `trans_date` 等が混在 | groupby 前に必要列のみに絞る |
140+
141+
---
142+
143+
## 2) 実装(改善版)
144+
145+
```python
146+
# Analyze Complexity
147+
# Runtime 371 ms
148+
# Beats 86.45%
149+
# Memory 69.30 MB
150+
# Beats 58.06%
151+
152+
import pandas as pd
153+
154+
def monthly_transactions(transactions: pd.DataFrame) -> pd.DataFrame:
155+
"""
156+
Returns:
157+
pd.DataFrame: 列名と順序は
158+
[month, country, trans_count, approved_count,
159+
trans_total_amount, approved_total_amount]
160+
"""
161+
# ★ copy() 廃止: 必要列のみで軽量 DataFrame を新規構築
162+
is_approved = (transactions['state'] == 'approved').astype('int8') # ★ int8 で省メモリ
163+
164+
tmp = pd.DataFrame({
165+
'month' : transactions['trans_date'].dt.to_period('M').astype(str),
166+
'country' : transactions['country'],
167+
'id' : transactions['id'],
168+
'amount' : transactions['amount'],
169+
'is_approved' : is_approved,
170+
'approved_amt' : transactions['amount'] * is_approved, # int8 × int → int
171+
})
172+
173+
out = (
174+
tmp.groupby(['month', 'country'], sort=False, dropna=False)
175+
.agg(
176+
trans_count = ('id', 'count'),
177+
approved_count = ('is_approved', 'sum'),
178+
trans_total_amount = ('amount', 'sum'),
179+
approved_total_amount = ('approved_amt', 'sum'),
180+
)
181+
.reset_index()
182+
)
183+
184+
out[['trans_count', 'approved_count',
185+
'trans_total_amount', 'approved_total_amount']] = \
186+
out[['trans_count', 'approved_count',
187+
'trans_total_amount', 'approved_total_amount']].astype(int)
188+
189+
return out
190+
```
191+
192+
---
193+
194+
## 改善効果の試算
195+
196+
| 指標 | 改善前 | 改善後(期待) |
197+
| -------------------- | -------------------- | ----------------------------------- |
198+
| `is_approved` メモリ | `int64`: 8 bytes/行 | `int8`: 1 bytes/行 → **87.5% 削減** |
199+
| `copy()` コスト | 全列複製 O(N×全列数) | **ゼロ**(新規構築のみ) |
200+
| groupby 対象列数 | 元 DataFrame の全列 | **6列のみ** |
201+
| Memory 期待値 | 69.39 MB | **~55 MB 以下**(Beats 70%+ 期待) |
202+
| Runtime 期待値 | 383 ms | **~300 ms 以下**(Beats 85%+ 期待) |

0 commit comments

Comments
 (0)