乙Py先生のプログラミング教室
初学者のためのプログラミング学習サイト

プログラミング

今回はアプリをリリースしたので
そちらのご報告です。

解説動画はこちら



アプリについて


今回作成したアプリはこちらです

・チャンクde英会話

iOS
AppStore


Android
Googleplay

良かったら使ってみてください


アプリの作成方法

Flutterを用いて作成しました

Flutterにした理由は
 1.iOSとAndroid両対応であること
 2.学習コストが低い
 3.文献が豊富で有る

以上の理由からFlutterにしてみました。

それ以外にもアプリを作る方法は
いくらでもありますが、今回は
Flutterを使ってみました。

開発言語がDartになるので
1から勉強することになります。

また、コードやデータなどの大半は
ChatGPTを用いて作成していますので
実質ChatGPTに頼れば
アプリの開発は容易かと思います。

学習開発の期間で2ヶ月くらいでした。



リリースについて

コードを実装しシミュレータや
実機でのテストが終わったら
ビルドを行なってアプリを作り
リリース準備ができます。


その前にアプリストアのアカウントがないと
そもそもリリース出来ないので
アプリストアのアカウント取得が必要です。

これもそこそこ手間と時間が掛かります。

リリース登録をしたら審査が行われますが
審査に通らなければビルドからやり直し
審査に通るまでの繰り返しです。

審査に通ったらようやく
リリース、アプリ配信ができる様になります。

リリース作業を始めてから
アカウント登録とアプリリリースまでで
大体1ヶ月くらい掛かっています。



Flutterについて

動画の方では少しだけ解説していますが
FlutterはiOSとAndroidの両方のアプリを
作成する事ができる開発フレームワークです。

Flutterをインストールしたら
VSCodeなどでコードを書き進める事が
できる様になります。

テストやビルドなども
Flutterコマンドを用いて行う形になります。

この辺りも
VSCodeと合わせておくと
開発が楽になるかなと思います。

どんな感じなのかは
動画の方で解説していますので
参考にしていただければと思います。


最後に

これからアプリ開発を行いたい方にとっては
色々な選択肢があると思いますが
Flutterを使ってアプリを開発したい方が
増えていただけたら幸いです。

Python言語の解説と共に
アプリ開発の方も進めていきますので
要望などあれば是非コメントいただければと思います。

それでは。

今回は名探偵コナンの
緋色の弾丸シミュレーションです。


解説動画はこちら




緋色の弾丸

『名探偵コナン 緋色の弾丸』

2021年4月16日公開のアニメ映画で
劇場版『名探偵コナン』シリーズの24作目です。

映画の中ではリニアの超高速移動に合わせて
数キロ以上の距離から、動く犯人の狙撃に成功しているシーンがあります

この弾丸の到達距離のシミュレーションを行なっていきます。


最長狙撃記録

現代の主要な最長狙撃記録です。
1位:約4,000m(2024年、ウクライナ軍、Snipex Alligator)
2位:3,800m(2023年、ウクライナ軍、MCR Horizon's Lord)
3位:3,540m(2017年、カナダ軍、TAC-50)

現実世界では3-4キロの到達が限界だと思われます。




映画上の設定

映画上には狙撃を成功させるための
重要なポイントとなる設定がいくつかありました。
世界初の「真空トンネル」内を走行する超電導リニアで時速は約1000km
ライフル弾には特注の「銀の弾丸」が使用された

リニアも弾丸も時速1000kmで飛んだ設定
リニアが減速した際に、ライフル弾は減速しないので、そのまま犯人に命中した

つまりは真空状態のリニア線路上を銀の弾丸が飛んでいったというのが
重要なポイントです。


銀の弾丸使用のポイント


狙撃用の弾の素材が銀が最良かどうかは分かりませんが
銀である必要はあったようです。


高い導電性:
銀は金属の中で電気を通しやすい

レンズの法則(渦電流):
導体である銀が、リニアの強力な変動磁場の中を高速で移動すると
弾丸表面に強力な「渦電流」が発生

磁気反発:
この渦電流が、レールの磁場と反発する磁力(ローレンツ力)を
生み出し、弾丸を浮かせる 「揚力」 となる


ということで
リニア線路内は重力の影響を受けない弾丸になる
と仮定することにします。


射撃シミュレーター

重力 0 や真空の状況を加味したものです

通常射撃(空気抵抗、重力影響有り)
真空射撃(空気抵抗 0、重力影響有り)
リニア射撃(空気抵抗 0、重力影響 0)
の状態を選べます。

コードはこちら
import numpy as np
import matplotlib.pyplot as plt
from math import pi, cos, sin, sqrt

class BallisticSimulator:
    def __init__(self, dt=0.01):
        self.dt = dt
        self.g = 9.80665

    def get_accelerations(self, vx, vy, k, am):
        """
        現在の速度と設定から、x方向とy方向の加速度を返す
        k: 空気抵抗係数, am: 磁気浮上加速度
        """
        v = sqrt(vx**2 + vy**2)
        # x方向: 空気抵抗のみ
        ax = -k * vx * v
        # y方向: 重力(下) + 磁気浮上(上) + 空気抵抗(上下)
        ay = -self.g + am - (k * vy * v)
        return ax, ay

    def simulate(self, mode="normal", v0_kmh=1000.0, deg=0.0, ht=1.5, mas_g=9.0, area_cm2=0.5):
        # モードごとの環境設定
        config = {
            "normal":      {"rho": 1.225, "am": 0.0},      # 1. 通常(空気あり、浮力なし)
            "vacuum":      {"rho": 0.0,   "am": 0.0},      # 2. 真空(空気なし、浮力なし)
            "maglev":      {"rho": 0.0,   "am": 9.80665}   # 3. 真空 + 磁気浮上
        }
        env = config.get(mode, config["normal"])
        v0 = v0_kmh / 3.6
        rad = deg * pi / 180
        # 物理定数の計算
        mas = mas_g / 1000.0
        s_area = area_cm2 / 10000.0
        cd = 0.3
        k = 0.5 * s_area * cd * env["rho"] / mas
        # 初期値
        x, y = 0.0, ht
        vx, vy = v0 * cos(rad), v0 * sin(rad)
        res_x, res_y = [x], [y]
        for _ in range(1000000):
            k1vx, k1vy = self.get_accelerations(vx, vy, k, env["am"])
            k2vx, k2vy = self.get_accelerations(vx + k1vx*self.dt/2, vy + k1vy*self.dt/2, k, env["am"])
            k3vx, k3vy = self.get_accelerations(vx + k2vx*self.dt/2, vy + k2vy*self.dt/2, k, env["am"])
            k4vx, k4vy = self.get_accelerations(vx + k3vx*self.dt, vy + k3vy*self.dt, k, env["am"])
            vx += (k1vx + 2*k2vx + 2*k3vx + k4vx) / 6 * self.dt
            vy += (k1vy + 2*k2vy + 2*k3vy + k4vy) / 6 * self.dt
            x += vx * self.dt
            y += vy * self.dt
            if y < 0 or x >= 300000: break
            res_x.append(x)
            res_y.append(y)
        return res_x, res_y

# --- 実行と可視化 ---
sim = BallisticSimulator()
modes = ["normal", "vacuum", "maglev"]
mode = modes[1]

rx, ry = sim.simulate(mode=mode, v0_kmh=1000.0, deg=0)
print(f"飛行時間  : {(len(rx) - 1) * sim.dt:.2f} 秒")
print(f"移動距離  : {rx[-1]:.2f} メートル, {rx[-1]/1000:.5f} キロメートル")
plt.figure(figsize=(16, 3))
plt.plot(rx, ry, label=f"Mode: {mode}")
plt.axhline(0, color='black', linewidth=1)
plt.title("Comparison of Bullet Trajectories (v=1000km/h)")
plt.xlabel("Distance (m)")
plt.ylabel("Height (m)")
plt.legend()
plt.grid(True)
plt.show()
飛行時間  : 0.54 秒
移動距離  : 150.00 メートル, 0.15000 キロメートル

download-3

 真空状態であったとしても
水平に撃ったのでは150メートルほどしか飛びません。


上の方に向けて撃った場合はもう少し長い距離飛びます。

download-2

射角5度くらいで1400メーターくらいです。
全然届かないですね。
しかも高さ30メートルの高低差が出てしまうので
リニアトンネルの天井にぶつかって終わってしまいます。


しかし、重力の影響が無いと仮定すると

download
銀の弾丸はまっすぐ飛んでいけます。



まとめ


弾丸が重力の影響を受けない限定的な条件下であれば
シミュレーション上ではトンネルが真っ直ぐ続く限り
銀の弾丸はどこまでも飛んでいくと推測されます。

狙撃距離は100kmあたり(2分後くらい)になると推測される

銀の弾丸に浮力が付き
リニアの線路がマジで真っ直ぐだったら
あり得ないでもない(赤井さんなら)
というのが結論です。


製作陣がどれだけの考慮をしたかは分かりませんが
特殊条件下であれば不可能でも無いと思います。

そういえば
来週からコナンの新作映画が公開されますね!!

速攻見にいきましょう


PS:
個人的には
逆襲のシャアの決着を
コナンで付けてもらいたかったのですが
もう叶わないのですかね






今回は数式から美しいアニメーションを作成できる
Manimのご紹介です

解説動画はこちら






Manimとは

Manim(Mathematical Animation Engine)
数学や物理を視覚的に表現するための
Pythonのアニメーションエンジンです。

3Blue1Brownの制作者(Grant Sanderson)によって開発
数学の教育動画で幅広く使用されています。

Python コードで数式やグラフをアニメーションとして
動画化してくれるライブラリです。


Manim を利用するうえで必要なこと

関連ライブラリのインストール
   - FFmpeg と必須ライブラリ
   - LaTeX (MacTeX) 
   - Manim本体
Python の知識
TeX 記法の知識

この辺りが必要です。


関連ライブラリのインストール

Manimを動かすには以下の3つが必要です。

1.FFmpeg と必須ライブラリ
2.LaTeX (MacTeX) 
3.Manim本体

早速インストールしていきましょう。

こちらはMac用のインストールコマンドです。
(Windowsの人はMacを買うか、Macを買いましょう)

1.FFmpeg と必須ライブラリのインストール

Manimが動画を描画・出力するために必要なツールです。
brew install ffmpeg py3cairo pkg-config


2.LaTeX (MacTeX) のインストール

美しい数式を描画するためのLaTeX環境です。
brew install --cask mactex-no-gui


3.Manim のインストール
pip install manim


インストール後の確認
manim --version



基本的な使い方

ファイルを作成して実行する

Pythonファイルとして作成した後
そのファイルをターミナルで実行することで
動画を作成できます。

manim -pqh ファイル名 シーン名


 - -p: レンダリング後に動画を自動再生
 - -q: 品質 (l=低, h=高, k=4K)
 - ファイル名 : hello.py
 - シーン名 : Hello


動画ファイルの出力先は
media/videos/...
になる(デフォルト)

hello.py というファイルでサンプルを用意しました。
# hello.py
from manim import *

class Hello(Scene):
    def construct(self):
        text = Text("どうもこんばんわ、乙pyです")
        self.play(Write(text))
        self.wait()

実行方法は次のようになります。

サンプルの実行
manim -pqh hello.py Hello




基本文法

インポートとコンストラクタの定義

ライブラリをインポートして
コンストラクタを最初に定義します。
from manim import *

class MyAnimation(Scene):
    def construct(self):


コンストラクタを作ったら
描きたいものを定義していきます。

変数名 = オブジェクト名(引数など)

self.play(): 
アニメーションを実行

.animate: 
オブジェクトのプロパティ(位置や色)を滑らかに変化

ReplacementTransform: 
前の物体を消して、次の物体へモーフィング

オブジェクトを生成して動かすサンプルも用意しました。

サンプルコード

# basicflow.py
# manim -pqh basicflow.py BasicFlow
from manim import *

class BasicFlow(Scene):
    def construct(self):
        # 1. オブジェクトの生成
        title = Text("Manimの基本操作", font_size=40)
        square = Square(color=BLUE, fill_opacity=0.5)
        circle = Circle(color=RED, fill_opacity=0.5)

        # 2. 配置の設定
        title.to_edge(UP) # 画面上端へ

        # 3. アニメーションの実行
        self.play(Write(title))        # 文字を書く
        self.play(DrawBorderThenFill(square)) # 枠線から塗りつぶし
        self.wait(1)

        # 4. 移動と変形 (Transform)
        self.play(
            square.animate.shift(LEFT * 2), # 左に動かしながら
            run_time=1
        )
        # 四角を円に変形させる
        self.play(ReplacementTransform(square, circle))
        self.play(circle.animate.shift(RIGHT * 4))
        
        self.wait(2)

オブジェクトを定義して配置し
アニメーション内容を書いていくことで
様々なアニメーションを作ることができます。




いろんなサンプルを見てみよう

5つの動画サンプルを用意しました。

1.正規分布のパラメータ変化
2.三角関数
3.フーリエ変換
4.ロジスティック写像
5.勾配降下法


どんなアニメーションを作れるかは
動画の方で確認してみてください。



まとめ


数学や物理系の綺麗なアニメーションを作るのは
かなりしんどいと思います。

このライブラリであれば、かなり綺麗な
アニメーションを作ることが出来るので
その道の動画を作りたい方は
試してみるのも面白いと思います。

それでは。









南海トラフ地震はいつ起きるのか
過去データから予測してみる事としました。

解説動画はこちら





南海トラフ地震とは

駿河湾から日向灘沖にかけてのプレート境界を震源域として
概ね100~150年間隔で繰り返し発生してきた大規模地震

静岡県から宮崎県にかけての一部では震度7となる可能性があります。

過去の発生年

発生年 名称
1946年 昭和南海地震
1944年 昭和東南海地震
1854年 安政南海地震
1707年 宝永地震
1605年 慶長地震
1498年 明応地震
1361年 正平南海地震


地震の発生確率の計算方法


最近の調査ではBPT分布というものを
用いた予測が基本になっています。

BPT分布
(Brownian Passage Time:ブラウン通過時間)
地震の発生間隔をモデル化する確率分布

確率的に「平均的な間隔」の周辺で起こるが
バラつきがあるというモデル


ポアソン分布は発生確率が常に一定(ランダム)とする一方
BPT分布は時間経過による「成熟度」を考慮したものです。



BPT分布の基本式

BPT分布の確率密度関数  𝑓(𝑡)  は、以下の式で表されます。
スクリーンショット 2026-03-14 17.25.49



条件付き確率の考え方

「前回の地震から  𝑡  年経過した時点で
今後  Δ𝑡  年以内に地震が発生する確率」

𝑃(𝑡,Δ𝑡)  は、以下の式で計算します。

スクリーンショット 2026-03-14 17.26.44


コード上では下記が確率計算部分になります。
prob = (f_future - f_t) / (1 - f_t)

分子:(f_future - f_t)
「現在からXX年の期間内に地震が発生する」という確率

分母:(1 - f_t)
「現在で、まだ地震が起きていない」という確率

BPTモデルでは時間が経つほど  𝐹(𝑡)  が大きくなるため
分母はどんどん小さくなっていき、確率が高まります。


Pythonで地震確率をシミュレーションする

あらかじめことわりますが
政府予測とは異なります!!!!


予測条件

平均再来間隔 (南海トラフの直近データ 1361 ~ 1946)
97.5

ばらつき係数 (地震調査委員会採用値)
0.24

前回の発生年 (昭和南海地震)
1946

今後30年間の地震発生確率を求めるコードは以下です。
import numpy as np
from scipy.stats import norm

def bpt_cdf_30(t, mu, alpha, delta_t=30):
    """
    BPT分布に基づき、今後 delta_t 年以内の地震発生確率を計算する
    
    Parameters:
    t: 前回からの経過年数
    mu: 平均再来間隔
    alpha: ばらつき係数
    delta_t: 評価期間(デフォルトは30年)
    """
    if t <= 0:
        return 0.0
    
    # 現在までの間に、既に地震が起きているはずだった累積確率
    inv_alpha_sq = 2 / (alpha**2)
    term1_t = norm.cdf((t / mu - 1) / (alpha * np.sqrt(t / mu)))
    term2_t = np.exp(inv_alpha_sq) * norm.cdf(-(t / mu + 1) / (alpha * np.sqrt(t / mu)))
    f_t = term1_t + term2_t
    
    # delta_t 年後までの間に、地震が起きる累積確率
    t_future = t + delta_t
    term1_f = norm.cdf((t_future / mu - 1) / (alpha * np.sqrt(t_future / mu)))
    term2_f = np.exp(inv_alpha_sq) * norm.cdf(-(t_future / mu + 1) / (alpha * np.sqrt(t_future / mu)))
    f_future = term1_f + term2_f
    
    if f_t >= 1.0:
        return 1.0
        
    prob = (f_future - f_t) / (1 - f_t)
    return max(0.0, min(prob, 1.0))

mu = 97.5       # 平均再来間隔 (南海トラフの直近データ 1361 ~ 1946)
alpha = 0.24    # ばらつき係数 (地震調査委員会採用値)
last_event = 1946  # 前回の発生年 (昭和南海地震)
target_year = 2026
t = target_year - last_event

# 30年分を予測
for delta_t in range(1, 31):
  prob = bpt_cdf_30(t, mu, alpha, delta_t)
  print(f"今後 {delta_t} 年以内の発生確率: {prob:.2%}")

今後30年間の発生確率は
ぜひ動画を見てみてください。




まとめ

地震の発生確率は確率分布をもとに
予測が行われています。

政府予測では
今後、30年の間に 60%~90%程度以上の確率で
南海トラフ地震来るという予測です

絶対は無いけど、明日来るかもしれない
いつ来ても良いように備えておきましょう!!!

それでは
 

今回はDuckDBを用いた
簡単SQLデータ分析についてです

解説動画はこちら



DuckDB

データ分析したいけどSQLならわかるんだけど
Python良く分からないなー

そんな時はDuckDB使えばいいじゃない!!

ということで
DuckDBのご紹介です。

Python上でSQLでデータ取得できるライブラリで
DataFrameをそのままSQLテーブルとして扱えます。

pandasの複雑なメソッドを覚えなくても
SQL操作で任意のデータが抽出できるものです。
pandasも加えると更に深掘りも出来てお得です。


早速使い方を見ていきましょう。


インストール

Google Colabでは
インストール済みなので不要です。

ローカルなどでインストールしていない人は
下記コマンドを実行しておいてください。
pip install duckdb


ライブラリの呼び出し

これだけです。
import duckdb


操作の基本
# 実行結果を表示
duckdb.sql("SQL文").show()

# 実行結果を変数に格納
データフレーム変数 = duckdb.sql("SQL文").df()

データフレームを作ってSQL文を投げてみます
import pandas as pd

df = pd.DataFrame({
    "a":[1,2,3],
    "b":[10,20,30]
})

sql = "SELECT * FROM df WHERE a > 1"

result_df = duckdb.sql(sql).df()
result_df

ab
0220
1330

DuckDBではデータフレームや
ファイルに直接SQL文のクエリを実行し
データ抽出が行えます。


SQL分をファイルから読み込みする
sql = open("ファイルパス").read()
df = duckdb.sql(sql).df()


ファイルを直接クエリする

sql = open("ファイルパス").read()
df = duckdb.sql(sql).df()
Google Colabなどでもファイルを置いておけば参照できます。



サンプルデータで簡易分析

sklearnサンプルデータを用いて
DuckDBでSQL分析してみましょう。

今回用いるデータは
「California Housing Dataset」

カリフォルニアの
地域ごとの住宅価格などのデータです。
下記コードで読み込みできます。
import duckdb
import pandas as pd
from sklearn.datasets import fetch_california_housing

# データロード
data = fetch_california_housing(as_frame=True)

df = data.frame

# DuckDBに接続
con = duckdb.connect()

# DataFrameを登録
con.register("housing", df)

データの準備が整ったら
DuckDBでSQLを実行してみましょう。

1.平均住宅価格
sql = """
SELECT
    AVG(MedHouseVal) as avg_price
FROM housing
"""

print(con.sql(sql).df())

2.住宅価格ランキング
sql = """
SELECT
    MedHouseVal
FROM housing
GROUP BY MedHouseVal
ORDER BY MedHouseVal DESC
LIMIT 10
"""

print(con.sql(sql).df())

3.地域ごとの住宅価格
sql = """
SELECT
    ROUND(Latitude,1) as lat,
    ROUND(Longitude,1) as lon,
    AVG(MedHouseVal) as avg_price,
    COUNT(*) as houses
FROM housing
GROUP BY lat, lon
ORDER BY avg_price DESC, houses DESC
LIMIT 15
"""

print(con.sql(sql).df())

4.収入と住宅価格
sql = """
SELECT
    CASE
        WHEN MedInc < 2 THEN 'low'
        WHEN MedInc < 5 THEN 'middle'
        ELSE 'high'
    END as income_level,
    AVG(MedHouseVal) as avg_price,
    COUNT(*) as cnt
FROM housing
GROUP BY income_level
ORDER BY avg_price DESC
"""

print(con.sql(sql).df())

5.家の広さと価格
sql = """
SELECT
    ROUND(AveRooms,0) as rooms,
    AVG(MedHouseVal) as price,
    COUNT(*) as n
FROM housing
GROUP BY rooms
HAVING n > 50
ORDER BY rooms
"""

print(con.sql(sql).df())


応用編

海沿い住宅は高いのかどうか

カリフォルニアは 西側が海なので
経度(Longitude) で判定


sql = """
SELECT
    CASE
        WHEN Longitude < -121 THEN 'coastal'
        ELSE 'inland'
    END AS location_type,
    AVG(MedHouseVal) AS avg_house_price,
    COUNT(*) AS houses
FROM housing
GROUP BY location_type
"""

result = con.sql(sql).df()

print("海沿い住宅(coastal) vs 内陸住宅(inland)")
print(result)

結果はコードを試すか
動画の方をご覧ください





まとめ


DuckDBを用いるとSQLを使ったデータ分析が
簡単に行えます。

Pythonのコードに慣れていなくても
直接ファイルにSQLを実行して分析結果を得られます。

複雑な集計もSQLを書ければ出来ちゃうので
データアナリストでなくても簡単に分析できちゃいます。

手元にデータが有るけどデータベース作れない
SQLは分かるけどpython分からない人向け
超お手軽ライブラリなので、おすすめです

ぜひ試してみてください
それでは。





 

このページのトップヘ