見出し画像

【Python学習ログ】NumPyの魔法、ユニバーサル関数(ufunc)を使ってみた!forループよ、さようなら


【導入】

こんにちは!Pythonでのデータ分析学習に奮闘中です。今回は、愛用している書籍「Pythonによるデータ分析入門」の第4章「NumPyの基本」の中でも、特に強力な機能である**ユニバーサル関数(ufunc)**について学びました。学習範囲は107ページから110ページにかけてです。

これまで、データの各要素に同じ計算をしたいときはforループを使うのが当たり前だと思っていましたが、NumPyの世界ではもっとスマートで、比較にならないほど高速な方法があるようです。AIとの対話を通じて、単純なコード練習から始まり、最終的には乱数の仕組みというコンピュータサイエンスの深い領域まで探求することになりました。その学びの軌跡をログとして残します。


【学習のゴール】


今回の学習ゴールは、シンプルかつ強力です。

「**ユニバーサル関数(ufunc)**という、NumPyでたくさんの計算を一瞬で終わらせるための魔法の道具を使いこなすこと!」

これをマスターすれば、配列(ndarray)に含まれる一つひとつのデータに対して、わざわざforループを書かなくても、平方根(x

​)や指数(ex)、最大値の比較といった計算を超高速で実行できるようになります。


【プロセスと発見】



単項ufunc:まずは基本の「き」


最初のステップは、一つの配列を引数にとる「単項ufunc」です。

教材に従い、まずは0から9までの整数を持つ配列を作成しました。これはnp.arange(10)ですぐに作れました。

Python

import numpy as np

arr = np.arange(10)

この配列arrに対して、全要素の平方根を求めるnp.sqrt()と、全要素を指数として計算するnp.exp()を試してみました。

Python

# 各要素の平方根を計算
print(np.sqrt(arr))

# 各要素をeの指数として計算
print(np.exp(arr))

実行結果は、一瞬で全ての計算が完了し、新しい配列として返ってきました。もしこれをPythonの標準機能だけでやろうとしたら、forループで要素を一つずつ取り出して計算し、新しいリストに追加する…といった手間がかかります。ufuncの簡潔さと速さに、最初から感動しました。


二項ufuncとエラーからの学び


次に挑戦したのは、二つの配列を引数にとる「二項ufunc」です。ここでは、二つの配列の同じ位置にある要素同士を比較し、大きい方だけを集めた新しい配列を作るnp.maximum()を使います。

まずは、比較対象となる2つのランダムな配列xとyを作成するところで、少しつまずきました。

Python

# 最初のコード(間違い)
x = np.random.random(8)
y = np.random.random(8)

print(np.maximum(x,y))

AIにレビューしてもらったところ、「おしい!教科書では標準正規分布に従うnp.random.randn()を使っていますよ」と指摘を受けました。ここで、ふと疑問が湧きました。

自分:「randomとrandnは違うんですか?」

この質問をきっかけに、二つの関数の違いがクリアになりました。

関数名生成される値の範囲分布(データのばらつき方)np.random.random0.0 以上、1.0 未満一様分布(どの値も同じ確率で出る)np.random.randnマイナス~プラスの無限大標準正規分布(0付近の値が多く、端に行くほど少ない)

データ分析では、測定誤差や自然現象のように、平均値の周りにデータが集まる「正規分布」を扱うことが多いため、randnがよく使われるとのこと。なるほど、と納得し、コードを修正しました。

Python

# 正しいコード
x = np.random.randn(8)
y = np.random.randn(8)

print(np.maximum(x, y))

無事に、二つの配列から要素ごとの最大値を抽出した配列を得ることができました。エラーや疑問から、より深い知識につながる良い経験でした。


複数の配列を返すufunc


最後に、少し変わったufuncとしてnp.modf()を試しました。これは、一つの配列を引数にとり、各要素を「小数部分」と「整数部分」に分解して、二つの配列として返してくれる関数です。

Python

arr2 = np.random.randn(7) * 5
print(arr2)
# 出力例: [ 3.84661053  1.51984874  5.09042654 ... ]

rem, whole = np.modf(arr2)

print(rem)    # 小数部分の配列
# 出力例: [ 0.84661053  0.51984874  0.09042654 ... ]

print(whole)  # 整数部分の配列
# 出力例: [ 3.  1.  5. ... ]

一つの関数から二つの成果物がタプル形式で返ってくるのが面白いです。この演習を通じて、AIにこんな感想を伝えていました。

自分:「確かに面白いですね。乱数使って掛け算とか足し算したら、そのデータを元に色々計算できそうで面白いです。」

この「面白い」という感覚が、学習を続ける上で本当に大切だと改めて感じました。


【最終的な成果物】


今回の一連の学習で作成した、各ufuncの動作を確認するコードです。

Python

import numpy as np

# --- 単項ufunc ---
arr = np.arange(10)
# 平方根
print("--- np.sqrt ---")
print(np.sqrt(arr))
# 指数
print("\n--- np.exp ---")
print(np.exp(arr))


# --- 二項ufunc ---
x = np.random.randn(8)
y = np.random.randn(8)
# 最大値
print("\n--- np.maximum ---")
print(f"x: {x}")
print(f"y: {y}")
print(f"result: {np.maximum(x, y)}")


# --- 複数の配列を返すufunc ---
arr2 = np.random.randn(7) * 5
# 整数部分と小数部分
print("\n--- np.modf ---")
print(f"original: {arr2}")
rem, whole = np.modf(arr2)
print(f"remainder: {rem}")
print(f"whole_part: {whole}")


【学びと今後の展望】

今回の学び(技術的なポイント)

  • ufuncの威力: forループを使わずに、配列全体の要素に対して一括で数学的な計算(平方根、指数、比較など)ができる。コードが簡潔になり、処理速度も圧倒的に速い。

  • ufuncの種類: 配列を一つとる単項ufunc(np.sqrt)、二つとる二項ufunc(np.maximum)、複数の配列を返すufunc(np.modf)など、様々な種類があることを学んだ。

  • np.random.randomとnp.random.randnの違い: 前者は0から1までの一様分布、後者は平均0・標準偏差1の標準正規分布に従う乱数を生成する。データ分析の文脈ではrandnが頻出することを理解した。

応用事例と考察(対話ログからの発見)

今回の学習は、単なるコーディング練習では終わりませんでした。AIとの対話の中で、自分の素朴な疑問が思わぬ探求の入り口となりました。

  • 整数論への応用: np.modfが整数と小数を分けるのを見て、「数学の整数論で使えそう」とコメントしたところ、AIがnp.mod(剰余)やnp.floor(切り捨て)といった他のufuncも、合同式の計算やガウス記号の計算など、整数論で強力なツールになることを教えてくれました。NumPyが純粋な数学の領域でも活用できるという視点は、大きな発見でした。

  • 乱数の正体: 「乱数はどうやって作られているのか?」という疑問から、コンピュータが生成するのは擬似乱数であるという話に発展しました。最初の値(シード)と計算式(アルゴリズム、例:メルセンヌ・ツイスター法)があれば、同じ乱数列を再現できるという仕組みです。さらに、「じゃあ逆算できるの?」と聞くと、「理論上は可能だが、内部状態が巨大で複雑なため現実的にはほぼ不可能」という答えが返ってきました。最終的には、予測不可能な物理現象(熱雑音など)を利用する真性乱数というものがあることまで知ることができ、コンピュータの奥深さに触れることができました。


学習方法への考察(対話ログからの発見)


今回、AIと対話しながら学習を進めることで、一つのテーマから知識が芋づる式に広がっていく面白さを体験しました。

  • 「なぜ?」を大切にする: randomとrandnの違いを疑問に思ったこと、乱数の仕組みに興味を持ったこと。これらの「なぜ?」という好奇心をAIにぶつけることで、教科書に書かれている内容以上の、生きた知識を得ることができました。

  • エラーは学びのチャンス: randomをrandnに修正するという小さなエラーも、二つの関数の使い分けを学ぶ絶好の機会になりました。

  • 対話が思考を深める: 自分が何気なく発した「整数論で使えそう」という一言から、AIが具体的な応用例を返してくれる。このやり取りを通じて、自分の考えが整理され、さらに深まっていくのを感じました。

ufuncという一つの機能から、乱数の仕組みというコンピュータサイエンスの基本まで学べた、非常に密度の濃い学習でした。これからもこの調子で、疑問を楽しみながら探求を続けていきたいと思います。

いいなと思ったら応援しよう!