Pythonで符号に応じて1, -1, 0を返すsign関数の実装方法を完全解説
sign関数とは
sign関数(符号関数)は、数値の符号を判定して以下のような値を返す数学関数です:
- 正の数の場合:1を返す
- 負の数の場合:-1を返す
- ゼロの場合:0を返す
# 基本的なsign関数の実装
def sign(x):
"""基本的な符号関数"""
if x > 0:
return 1
elif x < 0:
return -1
else:
return 0
# テスト
test_values = [5, -3, 0, 2.5, -1.7]
for val in test_values:
print(f"sign({val}) = {sign(val)}")
様々なsign関数の実装方法
条件分岐による実装
def sign_conditional(x):
"""条件分岐によるsign関数"""
if x > 0:
return 1
elif x < 0:
return -1
else:
return 0
# より簡潔な書き方
def sign_ternary(x):
"""三項演算子風のsign関数"""
return 1 if x > 0 else (-1 if x < 0 else 0)
数学的アプローチによる実装
def sign_mathematical(x):
"""数学的アプローチのsign関数"""
if x == 0:
return 0
return int(x / abs(x))
def sign_copysign(x):
"""math.copysignを使った実装"""
import math
if x == 0:
return 0
return int(math.copysign(1, x))
ワンライナー実装
# 様々なワンライナー実装
sign_lambda = lambda x: (x > 0) - (x < 0)
sign_bool = lambda x: int(x > 0) - int(x < 0)
sign_comparison = lambda x: (x > 0) * 1 + (x < 0) * (-1)
# テスト
test_val = -5
print(f"lambda版: {sign_lambda(test_val)}")
print(f"bool版: {sign_bool(test_val)}")
print(f"comparison版: {sign_comparison(test_val)}")
特殊なケースに対応したsign関数
浮動小数点数の特殊値対応
import math
def sign_robust(x):
"""浮動小数点の特殊値に対応したsign関数"""
if math.isnan(x):
return float('nan')
elif math.isinf(x):
return 1.0 if x > 0 else -1.0
elif x == 0.0:
return 0.0
else:
return 1.0 if x > 0 else -1.0
# 特殊値でのテスト
special_values = [5.0, -3.0, 0.0, float('inf'), float('-inf'), float('nan')]
for val in special_values:
result = sign_robust(val)
print(f"sign_robust({val}) = {result}")
複素数対応のsign関数
import math
def sign_complex(x):
"""複素数に対応したsign関数"""
if isinstance(x, complex):
if x == 0:
return 0
return x / abs(x) # 単位複素数を返す
else:
# 実数の場合は通常のsign関数
if x > 0:
return 1
elif x < 0:
return -1
else:
return 0
# 複素数でのテスト
complex_values = [3+4j, -2-3j, 5+0j, 0+0j, 0-2j]
for val in complex_values:
result = sign_complex(val)
print(f"sign_complex({val}) = {result}")
if isinstance(result, complex):
print(f" 絶対値: {abs(result)}")
NumPyスタイルのsign関数
NumPy互換実装
def numpy_sign(x):
"""NumPy風のsign関数(配列対応)"""
if hasattr(x, '__iter__'): # 配列やリストの場合
return [numpy_sign(item) for item in x]
else: # 単一値の場合
if x > 0:
return 1
elif x < 0:
return -1
else:
return 0
# 配列での使用例
values = [1, -2, 0, 3.5, -1.8]
results = numpy_sign(values)
print(f"入力: {values}")
print(f"結果: {results}")
# ネストした配列
nested_values = [[1, -2], [0, 3], [-1, 2]]
nested_results = numpy_sign(nested_values)
print(f"ネスト入力: {nested_values}")
print(f"ネスト結果: {nested_results}")
パフォーマンスを重視したsign関数
高速化実装
def sign_fast(x):
"""高速化されたsign関数"""
return (x > 0) - (x < 0)
def sign_bitwise(x):
"""ビット演算を利用した整数専用sign関数"""
if not isinstance(x, int):
raise TypeError("整数のみ対応")
if x == 0:
return 0
# 符号ビットを利用
return 1 if x > 0 else -1
# パフォーマンステスト
import time
def benchmark_sign_functions():
test_values = list(range(-1000, 1001))
iterations = 1000
# 条件分岐版
start = time.time()
for _ in range(iterations):
results = [sign_conditional(x) for x in test_values]
conditional_time = time.time() - start
# 高速版
start = time.time()
for _ in range(iterations):
results = [sign_fast(x) for x in test_values]
fast_time = time.time() - start
print(f"条件分岐版: {conditional_time:.6f}秒")
print(f"高速版: {fast_time:.6f}秒")
print(f"速度比: {conditional_time/fast_time:.2f}倍")
benchmark_sign_functions()
実用的なsign関数の活用例
データの正規化
def normalize_with_sign(data):
"""符号を保持した正規化"""
def sign(x):
return (x > 0) - (x < 0)
normalized = []
for val in data:
if val == 0:
normalized.append(0)
else:
normalized.append(sign(val) * (abs(val) / max(abs(x) for x in data if x != 0)))
return normalized
# テストデータ
original_data = [10, -5, 0, 15, -20, 3]
normalized = normalize_with_sign(original_data)
print(f"元データ: {original_data}")
print(f"正規化後: {normalized}")
勾配の方向判定
def gradient_direction(current_value, previous_value):
"""勾配の方向を判定"""
def sign(x):
return (x > 0) - (x < 0)
diff = current_value - previous_value
direction = sign(diff)
direction_names = {1: "上昇", -1: "下降", 0: "変化なし"}
return direction, direction_names[direction]
# 時系列データでの使用例
time_series = [10, 12, 11, 15, 15, 13, 18]
print("時系列データの勾配方向:")
for i in range(1, len(time_series)):
direction, name = gradient_direction(time_series[i], time_series[i-1])
print(f" {time_series[i-1]} → {time_series[i]}: {direction} ({name})")
制御システムでの符号判定
def control_system_sign(error, threshold=0.1):
"""制御システム用のsign関数(デッドゾーン付き)"""
if abs(error) < threshold:
return 0 # デッドゾーン内
return 1 if error > 0 else -1
def pid_controller_simple(error_history, kp=1.0, ki=0.1, kd=0.01):
"""簡単なPID制御でのsign関数使用例"""
if len(error_history) < 2:
return 0
current_error = error_history[-1]
previous_error = error_history[-2]
# P項
p_term = kp * current_error
# I項
i_term = ki * sum(error_history)
# D項
d_term = kd * (current_error - previous_error)
control_signal = p_term + i_term + d_term
# 符号による方向判定
direction = control_system_sign(control_signal)
return control_signal, direction
# 制御システムのテスト
errors = [0.5, 0.3, 0.1, -0.2, -0.4]
print("PID制御シミュレーション:")
for i in range(2, len(errors)+1):
signal, direction = pid_controller_simple(errors[:i])
print(f"誤差履歴: {errors[:i]}")
print(f"制御信号: {signal:.3f}, 方向: {direction}")
print()
エラーハンドリング付きsign関数
堅牢なsign関数
import math
def sign_safe(x, handle_special=True):
"""エラーハンドリング付きsign関数"""
try:
# 型チェック
if not isinstance(x, (int, float, complex)):
raise TypeError(f"数値型が必要です。受信型: {type(x)}")
# 複素数の場合
if isinstance(x, complex):
if x == 0:
return 0
return x / abs(x)
# 特殊値の処理
if handle_special:
if math.isnan(x):
return float('nan')
elif math.isinf(x):
return 1.0 if x > 0 else -1.0
# 通常のsign処理
if x > 0:
return 1
elif x < 0:
return -1
else:
return 0
except Exception as e:
print(f"sign_safe エラー: {e}")
return None
# エラーハンドリングのテスト
test_cases = [
5,
-3,
0,
float('inf'),
float('nan'),
3+4j,
"文字列",
None
]
print("エラーハンドリングテスト:")
for case in test_cases:
result = sign_safe(case)
print(f"sign_safe({case}) = {result}")
カスタマイズ可能なsign関数
パラメータ化されたsign関数
def sign_custom(x, positive_val=1, negative_val=-1, zero_val=0, threshold=0):
"""カスタマイズ可能なsign関数"""
if abs(x) <= threshold:
return zero_val
elif x > threshold:
return positive_val
else:
return negative_val
# 様々なカスタマイズ例
test_value = 0.05
# 標準のsign関数
standard = sign_custom(test_value)
print(f"標準: sign_custom({test_value}) = {standard}")
# デッドゾーン付き
with_deadzone = sign_custom(test_value, threshold=0.1)
print(f"デッドゾーン: sign_custom({test_value}, threshold=0.1) = {with_deadzone}")
# カスタム戻り値
custom_values = sign_custom(5, positive_val="正", negative_val="負", zero_val="ゼロ")
print(f"カスタム戻り値: sign_custom(5, 'positive', 'negative', 'zero') = {custom_values}")
# スケール調整
scaled = sign_custom(-3, positive_val=10, negative_val=-10)
print(f"スケール調整: sign_custom(-3, 10, -10) = {scaled}")
科学計算での応用
数値微分でのsign関数
import math
def numerical_derivative(f, x, h=1e-8):
"""数値微分"""
return (f(x + h) - f(x - h)) / (2 * h)
def find_critical_points(f, x_range, step=0.1):
"""臨界点(極値)の近似的な発見"""
def sign(x):
return (x > 0) - (x < 0)
critical_points = []
x = x_range[0]
prev_derivative = numerical_derivative(f, x)
while x <= x_range[1]:
current_derivative = numerical_derivative(f, x)
# 微分の符号が変わった点を記録
if sign(prev_derivative) != sign(current_derivative):
critical_points.append(x)
prev_derivative = current_derivative
x += step
return critical_points
# テスト関数: f(x) = x^3 - 6x^2 + 9x + 1
def test_function(x):
return x**3 - 6*x**2 + 9*x + 1
# 臨界点を探す
critical_pts = find_critical_points(test_function, (-1, 5), step=0.1)
print(f"臨界点(近似): {critical_pts}")
# 各点での関数値と微分値を確認
for pt in critical_pts:
f_val = test_function(pt)
df_val = numerical_derivative(test_function, pt)
print(f"x={pt:.1f}: f(x)={f_val:.3f}, f'(x)={df_val:.6f}")
ベクトル演算でのsign関数
2Dベクトルの象限判定
def vector_quadrant_sign(vector):
"""2Dベクトルの象限をsign関数で判定"""
def sign(x):
return (x > 0) - (x < 0)
if len(vector) != 2:
raise ValueError("2Dベクトルが必要です")
x, y = vector
sign_x = sign(x)
sign_y = sign(y)
quadrant_map = {
(1, 1): "第1象限",
(-1, 1): "第2象限",
(-1, -1): "第3象限",
(1, -1): "第4象限",
(0, 1): "正のy軸",
(0, -1): "負のy軸",
(1, 0): "正のx軸",
(-1, 0): "負のx軸",
(0, 0): "原点"
}
return (sign_x, sign_y), quadrant_map.get((sign_x, sign_y), "不明")
# ベクトルでのテスト
test_vectors = [
(3, 4),
(-2, 5),
(-1, -3),
(2, -1),
(0, 3),
(4, 0),
(0, 0)
]
print("ベクトル象限判定:")
for vec in test_vectors:
signs, quadrant = vector_quadrant_sign(vec)
print(f"ベクトル{vec}: 符号{signs} → {quadrant}")
まとめ
Pythonにおけるsign関数の実装は、用途に応じて様々なアプローチが可能です。最もシンプルで高速な実装は (x > 0) - (x < 0) ですが、浮動小数点の特殊値や複素数、エラーハンドリングが必要な場合は、より詳細な実装が必要になります。数値計算、制御システム、信号処理など、多くの分野でsign関数は基本的なツールとして活用され、適切な実装を選択することで効率的なプログラムを作成できます。
■プロンプトだけでオリジナルアプリを開発・公開してみた!!
■AI時代の第一歩!「AI駆動開発コース」はじめました!
テックジム東京本校で先行開始。
■テックジム東京本校
「武田塾」のプログラミング版といえば「テックジム」。
講義動画なし、教科書なし。「進捗管理とコーチング」で効率学習。
より早く、より安く、しかも対面型のプログラミングスクールです。
<短期講習>5日で5万円の「Pythonミニキャンプ」開催中。
<月1開催>放送作家による映像ディレクター養成講座
<オンライン無料>ゼロから始めるPython爆速講座


