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爆速講座