Python 時間波形を時間方向にシフトさせるコード

Programming
スポンサーリンク

はじめに

Python で1次元の numpy.ndarray 型で時間波形を処理していて、波形を時間方向にシフトさせるコードを書こうとしたのですが、サンプリング時間からインデックスを求めてスライスしてとかやるのが面倒だったので、いい方法がないか Chat GPT に聞いて実装しました。

波形を時間方向にシフトさせるコード

前提としては、シフトさせたときに、 np.roll のように配列ではみ出した部分が逆側に回るということがないようにしたいと思っていました。

実装の方法としては4つありそうです。以下にPythonのコードを記載します。

  • shift_waveform_interpolation
    • numpy.interp を利用する方法
  • shift_waveform_ndimage
    • scipy.ndimage.shift を利用する方法
  • shift_waveform
    • numpy のスライスを利用する方法
  • shift_waveform_concatenation
    • numpy.concatenatenumpy のスライスを組み合わせる方法

どれも波形から見ると同じように見えましたが、実際に同じ値になっているのは、shift_waveformshift_waveform_concatenation の結果だけでした。

これらはスライスで取ってきてるので元のデータと同じになります。一方で、他の処理は内部で補間などがされていると考えられますが、詳細は確認できていません。

import numpy as np
import matplotlib.pyplot as plt

from scipy.ndimage import shift
from typing import List


def shift_waveform_interpolation(
    original_waveform: np.ndarray, shift_time: float, sampling_time: float
) -> np.ndarray:
    """
    Shift a waveform using interpolation.

    Parameters
    ----------
    original_waveform : np.ndarray
        The original waveform.
    shift_time : float
        The amount of time by which the waveform should be shifted.
    sampling_time : float
        The sampling time of the waveform.

    Returns
    -------
    out : np.ndarray
        The shifted waveform.
    """
    t = np.arange(0, len(original_waveform) * sampling_time, sampling_time)
    t_shifted = t + shift_time
    return np.interp(t, t_shifted, original_waveform, left=0, right=0)


def shift_waveform_ndimage(
    waveform: np.ndarray, shift_time: float, sampling_time: float
) -> np.ndarray:
    """
    Shift a waveform using scipy.ndimage's shift function.

    Parameters
    ----------
    waveform : np.ndarray
        The original waveform.
    shift_time : float
        The amount of time by which the waveform should be shifted.
    sampling_time : float
        The sampling time of the waveform.

    Returns
    -------
    out : np.ndarray
        The shifted waveform.
    """
    shift_amount = int(shift_time / sampling_time)
    return shift(waveform, shift_amount, cval=0)


def shift_waveform(
    waveform: np.ndarray, shift_time: float, sampling_time: float
) -> np.ndarray:
    """
    Shift a waveform by a specified time.

    Parameters
    ----------
    waveform : np.ndarray
        The original waveform.
    shift_time : float
        The amount of time by which the waveform should be shifted.
    sampling_time : float
        The sampling time of the waveform.

    Returns
    -------
    out : np.ndarray
        The shifted waveform.
    """
    # シフト量をサンプル数に変換
    shift_amount = int(shift_time / sampling_time)

    # 0で埋められた配列を用意
    shifted_waveform = np.zeros_like(waveform)

    # シフト量が正なら右に、負なら左にシフト
    if shift_amount > 0:
        shifted_waveform[shift_amount:] = waveform[:-shift_amount]
    elif shift_amount < 0:
        shifted_waveform[:shift_amount] = waveform[-shift_amount:]

    return shifted_waveform


def shift_waveform_concatenation(
    original_waveform: np.ndarray, shift_time: float, sampling_time: float
) -> np.ndarray:
    """
    Shift a waveform by concatenating zeros.

    Parameters
    ----------
    original_waveform : np.ndarray
        The original waveform.
    shift_time : float
        The amount of time by which the waveform should be shifted.
    sampling_time : float
        The sampling time of the waveform.

    Returns
    -------
    out : np.ndarray
        The shifted waveform.
    """
    # シフト量をサンプル数に変換
    shift_amount = int(shift_time / sampling_time)

    # 0で埋められた配列を用意し、元の波形を連結
    shifted_waveform = np.concatenate(
        [np.zeros(shift_amount), original_waveform[:-shift_amount]]
    )

    return shifted_waveform


def plot_shifted_waveforms(
    original_waveform: np.ndarray,
    shifted_waveforms: List[np.ndarray],
    shift_times: List[float],
    method_names: List[str],
) -> None:
    """
    Plot the original and shifted waveforms obtained using different methods.

    Parameters
    ----------
    original_waveform : np.ndarray
        The original waveform.
    shifted_waveforms : List of np.ndarray
        List of shifted waveforms obtained using different methods.
    shift_times : List of float
        List of shift times for each method.
    method_names : List of str
        List of names for each method.
    """
    t = np.arange(0, len(original_waveform) * 0.1, 0.1)

    plt.plot(t, original_waveform, label="Original Waveform")

    for shifted_waveform, shift_time, method_name in zip(
        shifted_waveforms, shift_times, method_names
    ):
        plt.plot(
            t,
            shifted_waveform,
            label=f"{method_name} Shift by {shift_time}s",
        )

    plt.xlabel("Time")
    plt.ylabel("Amplitude")
    plt.legend()
    plt.show()


# オリジナルのガウス波形を生成
t = np.arange(0, 100, 0.1)
original_gaussian = np.exp(-((t - 50) ** 2) / (2 * 10**2))

# サンプリング時間
sampling_time = 0.1

# シフトする時間 T[s]
shift_time = 50

# 各関数で波形をシフト
shifted_waveform_interp = shift_waveform_interpolation(
    original_gaussian, shift_time, sampling_time
)
shifted_waveform_ndimage = shift_waveform_ndimage(
    original_gaussian, shift_time, sampling_time
)
shifted_waveform_custom = shift_waveform(original_gaussian, shift_time, sampling_time)
shifted_gaussian_concatenation = shift_waveform_concatenation(
    original_gaussian, shift_time, sampling_time
)

# 波形を比較してプロット
plot_shifted_waveforms(
    original_gaussian,
    [
        shifted_waveform_interp,
        shifted_waveform_ndimage,
        shifted_waveform_custom,
        shifted_gaussian_concatenation,
    ],
    [shift_time, shift_time, shift_time, shift_time],
    ["Interpolation", "ndimage", "Custom", "Concatennation"],
)

print(np.all(shifted_waveform_interp == shifted_waveform_ndimage))  # False
print(np.all(shifted_waveform_interp == shifted_waveform_custom))  # False
print(np.all(shifted_waveform_interp == shifted_gaussian_concatenation))  # False
print(np.all(shifted_waveform_ndimage == shifted_waveform_custom))  # False
print(np.all(shifted_waveform_ndimage == shifted_gaussian_concatenation))  # False
print(np.all(shifted_waveform_custom == shifted_gaussian_concatenation))  # True

おわりに

とりあえず、波形を時間方向にシフトさせるコードを作りたかったので、書きました。

複数の実装方法があることを Chat GPT から学べたのと、計算方法が違うときは内部までしっかり理解することが必要だなと感じました。

コメント

タイトルとURLをコピーしました