『Python数値計算ノート』ではアフィリエイトプログラムを利用して商品を紹介しています。

絶対値の演算

Pythonで絶対値を計算する

実数 $x$ の絶対値 $|x|$ は数直線上の原点からの距離を表します:
 \[|x|=\begin{cases}x & (x\geq 0)\\[6pt]-x & (x\lt 0)\end{cases}\]
すなわち、正数の絶対値はもとの数と同じですが、負数の絶対値はマイナス符号を取り去って正数となります。Python の組み込み関数 abs() を使って、$-10$ の絶対値が $10$ となることを確認してみましょう。

# PYTHON_ABSOLUTE

# In[1]

# -10の絶対値を計算
x = abs(-10)

print(x)
# 10

複素数 $z=a+bi$ の絶対値は複素平面上の原点からの距離として定義されます:
 \[|z|=\sqrt{a^2+b^2}\]
たとえば、$2+3i$ の絶対値は
 \[\sqrt{2^2+3^2}=\sqrt{13}=3.60555\,…\]
となります。

# In[2]

# 2+3iの絶対値を計算
z = abs(2+3j)

print(z)
# 3.605551275463989

複素数の絶対値の定義は、実数の絶対値の定義を包含します。たとえば、$10=10+0i$ の絶対値は
 \[\sqrt{10^2+0^2}=10\]
となります。

NumPy パッケージの numpy.absolute() 関数を使うと、配列の各要素の絶対値をまとめて取得できます。

# In[3]

import numpy as np

# 配列を定義
arr = np.array([0, 1, -1])

# 配列の各要素の絶対値を計算
arr_abs = np.abs(arr)

print(arr_abs)
# [0 1 1]

numpy.absolute() と Matplotlib を使って、$y=|x|$ のグラフを描いてみます。

# In[4]

import matplotlib.pyplot as plt

# データを作成
x = np.linspace(-4, 4, 33)
y = np.abs(x)

# FigureとAxes
fig = plt.figure()
ax = fig.add_subplot(111)
ax.grid()
ax.set_title("y = |x|", fontsize=16, pad=10)
ax.set_xlabel("x", fontsize=16)
ax.set_ylabel("y", fontsize=16)
ax.set_xlim(-4, 4)
ax.set_ylim(-1, 4)

# y=|x|を青色ラインでプロット
ax.plot(x, y, color="blue")

plt.show()

Python 絶対値のグラフ
同様にして、関数 $y=f(x)$ の絶対値 $y=|f(x)|$ のグラフを描けます。グラフは $f(x)$ で負になる部分を $x$ 軸対称に折り返した形になります。たとえば、次のコードは、$y=\sin x$ と $y=|\sin x|$ のグラフを表示します。

# In[5]

import matplotlib.pyplot as plt

# 円周率の表記を設定
pi = np.pi

# データ(x,y)を設定
x = np.arange(0, 2*pi, 0.1)
y1 = np.sin(x)
y2 = np.abs(np.sin(x))  # 正弦関数の絶対値

# FigureとAxesを設定
fig = plt.figure()
ax = fig.add_subplot(111)
ax.grid()
ax.set_title("y = |sinx|", fontsize=16, pad=10)
ax.set_xlabel("x", fontsize=16)
ax.set_ylabel("y", fontsize=16)
ax.set_xlim(0, 2 * pi)
ax.set_ylim(-1.5, 1.5)
ax.set_xticks([0, pi/2, pi, 3*pi/2, 2*pi])
ax.set_xticklabels(["0", "$\pi/2$", "$\pi$", "$3\pi/2$", "$2\pi$"],
                   fontsize = 12)

# データをプロット
ax.plot(x, y1, linestyle="--", color="blue", label="y = sinx")
ax.plot(x, y2, color="red", label="y = |sinx|")
ax.legend()

plt.show()

Python 三角関数の絶対値 |sinx|
実数 $|x|$ の絶対値は、$x_1$ と $x_2$ の最大値 (小さくないほうの値) をとる関数 $\max()$ を使って、
 \[|x|=\max(x,-x)\]
と表すこともできます。$x$ と $-x$ のうち、片方は必ず正の値となるからです。たとえば、$x=-13$ のときは
 \[\max(-13,13)=13\]
となって、$|-13|$ と同値です。組み込み関数 max() を使って確認してみましょう。

# In[6]

# -13の絶対値
y = max(-13, 13)

print(y)
# 13

とはいえ、複素数の大きさは比較できないので、$\max()$ 関数による定義は引数が実数である場合に限定されます。
 
実数 $x$ を $|x|$ で割ると、$x$ の符号 ($+1$ または $-1$) が得られます。すなわち、$f(x)=x/|x|$ は符号関数です (ただし、$0$ では除算できないので、$x=0$ のときは $f(0)=0$ とします)。Python で符号関数を定義してみましょう。

# In[7]

# 符号関数を定義
def sign(x):
    if x == 0:
        return 0
    else:
        return x / abs(x)

# -23の符号を得る
val = sign(-23)

print(val)
# -1.0

Python 本体、および各種ライブラリで使用できる絶対値関数を以下にまとめておきます。

abs()

Python 本体に組み込まれている abs(x) は引数 x の絶対値を返します。x に整数を渡すと整数型を返します。

# PYTHON_ABS

# In[1]

# 整数の絶対値
x = abs(-100)

print(x)
# 100

浮動小数点数を渡すと、浮動小数点数型で値を返します。

# In[2]

# 浮動小数点数の絶対値
x = abs(-100.0)

print(x)
# 100.0

複素数 a + bj を渡すと、引数の絶対値を浮動小数点数型で返します。

# In[3]

# 複素数の絶対値
x = abs(5 + 7j)

print(x)
# 8.602325267042627

math.fabs()

標準ライブラリの mathモジュールをインポートすると、math.fabs(x) を使えるようになります。 math.fabs(x) は引数 x の絶対値を返します。引数に整数、浮動小数点数のいずれを渡しても、戻り値は浮動小数点数型となります。

# PYTHON_MATH_FABS

# In[1]

import math

# 整数型の絶対値
x = math.fabs(-100)

print(x)
# 100.0

math.fabs() に複素数を渡すと TypeError が返ります。

# In[2]

# math.fabs(complex)
x = math.fabs(1+2j)

print(x)
# TypeError: can't convert complex to float

numpy.absolute()

numpy.absolute(x) は引数 x の絶対値を返します。この関数は numpy.abs(x) という略記で使用することもできます。引数 x には整数、浮動小数点数、複素数、配列 (ndarray) を指定することができます。

# NUMPY_ABSOLUTE

import numpy as np

# 配列を定義
x = np.array([5, -5, 7 + 9j])

# 配列xの各要素の絶対値を計算
x_abs = np.abs(x)

print(x_abs)
# [5.          5.         11.40175425]

引数に渡す配列の個々の要素の型が異なっている場合は、最も広い型をもつ要素と同型に揃えた配列を返します。上のサンプルでは、配列 x のなかに複素数型の要素があるので、この配列の絶対値をとると、要素をすべて複素数型に揃えた配列が返ってきます。

numpy.fabs()

numpy.fabs(x) は引数 x の絶対値を返します。引数 x には整数、浮動小数点数、(整数と浮動小数点数を要素にもつ) 配列を指定することができますが、複素数や複素数を要素にもつ配列を渡すことはできません。

# NUMPY_FABS

import numpy as np

# 配列を定義
x = np.array([5, -5, -10.0])

# 配列xの各要素の絶対値を計算
x_abs = np.fabs(x)

print(x_abs)
# [5. -5. -10.]

mpmath.fabs()

mpmath.fabs(x) は x の絶対値を返します。

# MPMATH_FABS

# In[1]

from mpmath import *
mp.dps = 10
mp.pretty = True

# -10の絶対値
print(fabs(-10))
# 10

複素数の絶対値も計算できます。

# In[2]

# 3+5iの絶対値
z = fabs(3 + 5j)

print(z)
# 5.830951895

sympy.Abs()

SymPy パッケージをインポートすると、sympy.Abs(x) を使って数値や文字式の絶対値を計算できます。x には数値または記号を含む複素数を渡すことができます。

# SYMPY_ABS

# In[1]

from sympy import Abs, I, symbols

# 記号x,yを定義
x, y = symbols('x y')

# f(x,y)=|x+iy|
f = Abs(x + I*y)

# f(1,1)
val = f.subs([(x, 1), (y, 1)])

print(val)
# sqrt(2)

絶対値記号を含む方程式を解くこともできます。たとえば、
 \[|x+3|=7\]
という方程式を考えてみましょう。手計算で解く場合、絶対値記号の中身の正負によって場合分けします。$x+3 \geq 0$ すなわち $x \geq -3$ のとき、
 \[x+3=7\]
より $x=4$ が解であり、$x+3 \lt 0$ すなわち $x \lt -3$ のとき、
 \[-x-3=7\]
より、解は $x=-10$ となります。SymPy に解かせて確認してみましょう。記号 x を定義するとき、x を実数に制限するために、real=True を指定しておきます。

# In[2]

from sympy import solve

# 記号xを定義
x = symbols("x", real=True) 

# 方程式 |x+3|=7 を解く
solutions = solve(Abs(x + 3) - 7, x)

print(solutions)
# [-10, 4]

絶対値関数の微分

絶対値関数 $y=|x|$ は $x=0$ で「尖って」いるので、一般的にこの関数は $x=0$ で微分不可能とされます。しかし、
 \[\lim_{h \to 0}\frac{f(x+h)-f(x-h)}{2h}\]
によって定義される微分を考えるとき、絶対値関数は $x=0$ においても微分可能で、その値は $0$ となります。このような微分を対称微分とよびます。極限をとらない近似式は中心差分公式とよばれ、数値計算で多用されます。たとえば、SymPy で絶対値関数の微分を実行すると、符号関数 $\mathrm{sign}(x)$ を返すようになっています (≫ 符号関数の詳細についてはこちらの記事を参照してください)。

# ABS_FUNCTION

# In[1]

import sympy as sym

# 実行結果をTeXで表示する
sym.init_printing()

# 数式で使用するシンボルを定義
sym.var('x', real=True)

# y=|x|を微分
expr = sym.Abs(x).diff(x)

expr
\[\operatorname{sign}{\left(x \right)}\]

sympy.plot() で符号関数 ($y=|x|$ の導関数) のグラフを描いてみます。

# In[2]

# sign(x)をプロット
sym.plot(dydx, line_color="navy")

SymPy plot sign(x)
$\mathrm{sign}(x)$ は $x \gt 0$ で $1$、$x \lt 0$ で $-1$ の値をとります (それぞれの領域における $y=|x|$ の傾きです)。そして、$x=0$ においては $\mathrm{sign}(0)=0$ と定義されています ($y=|x|$ の対称微分が $0$ になるのに対応しています)。
 
次はもっと複雑な絶対値関数
 \[y=|x^2-3x-5|\]
の導関数を調べてみましょう。以下のコードを実行すると、導関数を表示し、もとの関数と導関数を色分けして描画します。

# In[3]

# y=|x**2-3x-5|
y = sym.Abs(x**2 - 3*x - 5)

# yを微分する
dy_dx = y.diff(x)

# 導関数を表示
display(dy_dx)

# 関数と導関数をプロット
ax = sym.plot(y, dy_dx, (x,-5,5), legend=True, show=False)
ax[0].line_color = "blue"
ax[1].line_color = "red"

ax.show()
\[\left(3 – 2 x\right) \operatorname{sign}{\left(- x^{2} + 3 x + 5 \right)}\]

sympy plot Abs(x^2-3x-5)
実行結果を見ると、やはり導関数は符号関数を含む式となっていて、対称微分が $0$ になる箇所が 2 点あります。

コメント

  1. へのへのもへじ より:

    print(“x_abs =”, x) ではなくてprint(“x_abs =”, x_abs)では?同様なミスが何か所かあります。

  2. HNaito より:

    下記は誤植と思われますので、ご確認ください。
     
    ABSOLUTE In[4] プログラムの上の文で、Matplotlib( ) → Matplotlib
    ABS In[3] プログラムで、5 + 7J → 5 + 7j
     ※7Jでもエラーにはならず、正しく計算はされます。
     
    sympy の solve( )、diff( ) は結構強力なツールのようですね。

    • あとりえこばと より:

      ありがとうございます。修正しました。
      複素数リテラル j は大文字でも認識されるんですね … 初めて知りました。
      おっしゃるように、sympy の solve() や diff() は便利なので検算などに活用しています。本文記事にもあるように、本来であれば微分不可能な尖った点でも対称微分の定義を使って結果を sign 関数で返してきたのは驚きでした。SymPy は純粋数学に限らず量子力学など高度な物理学の計算機能も備えています。このサイトではとても全部は扱いきれませんが、これからもできるだけ色々な機能を紹介しようと思います。

  3. あとりえこばと より:

    【技術英語の豆知識】絶対値を英語で “absolute value” といいます。absolute は「絶対の、不変な、完全な」を意味する形容詞で、科学分野では
    ・absolute temperature 絶対温度
    ・absolute zero (絶対零度:-273.15C)
    などの用語で用いられます。

    Python の組み込み関数 abs() は absolute value の略語です。math.fabs() や numpy.fabs() は戻り値が浮動小数点 (float value) に限定されるという意味で、abs の頭に f をつけています。