додавання шуму до сигналу в python


95

Я хочу додати випадковий шум до якогось 100-бінового сигналу, який я імітую в Python, - щоб зробити його більш реалістичним.

На базовому рівні, моя перша думка полягала в тому, щоб переходити по смітникам і просто генерувати випадкове число між певним діапазоном і додавати або віднімати це від сигналу.

Я сподівався (оскільки це python), що може бути більш розумний спосіб зробити це за допомогою numpy чи чогось іншого. (Я вважаю, що в ідеалі також було б кращим число, взяте з гауссового розподілу і додане до кожного смітника.)

Дякую заздалегідь за будь-які відповіді.


Я лише на стадії планування свого коду, тому мені немає чого показувати. Я просто думав, що, можливо, існує більш досконалий спосіб утворення шуму.

Що стосується вихідних даних, якби у мене було 10 бункерів із такими значеннями:

Кошик 1: 1 Кошик 2: 4 Кошик 3: 9 Кошик 4: 16 Кошик 5: 25 Кошик 6: 25 Кошик 7: 16 Кошик 8: 9 Кошик 9: 4 Кошик 10: 1

Я просто подумав, чи існує заздалегідь визначена функція, яка може додавати шум, щоб отримати щось на зразок:

Кошик 1: 1,13 Кошик 2: 4,21 Кошик 3: 8,79 Кошик 4: 16,08 Кошик 5: 24,97 Кошик 6: 25,14 Кошик 7: 16,22 Кошик 8: 8,90 Кошик 9: 4,02 Кошик 10: 0,91

Якщо ні, я просто перейду по коду та додам до кожного число, вибране з гауссового розподілу.

Дякую.


Це насправді сигнал від радіотелескопа, який я імітую. Я хочу мати можливість врешті-решт вибрати співвідношення сигнал / шум мого моделювання.


2
Будь ласка, продемонструйте код, який ви спробували, або конкретну проблему, з якою ви стикаєтесь. Зразки вхідних даних та бажаний результат також пройшли б довгий шлях.
gddc

2
Що це за сигнал? Який шум ви хочете ввести? "Реалістичний" залежить від типу сигналу. Аудіошум не є таким, як шум зображення, наприклад.
Дієго Баш

Відповіді:


126

Ви можете створити шумовий масив і додати його до свого сигналу

import numpy as np

noise = np.random.normal(0,1,100)

# 0 is the mean of the normal distribution you are choosing from
# 1 is the standard deviation of the normal distribution
# 100 is the number of elements you get in array noise

17
У деяких контекстах може мати сенс помножити ваш сигнал на масив шумів (з центром близько 1), а не додавати шумовий масив, але, звичайно, це залежить від природи шуму, який ви намагаєтеся імітувати.
Едвард Лопер

69

... А для тих, хто - як я - дуже рано на своїй дурній кривій навчання,

import numpy as np
pure = np.linspace(-1, 1, 100)
noise = np.random.normal(0, 1, 100)
signal = pure + noise

59

Для тих, хто намагається встановити зв’язок між SNR та нормальною випадковою величиною, що генерується numpy:

[1] Коефіцієнт SNR, де важливо мати на увазі, що Р - середня потужність.

Або в дБ:
[2]SNR дБ2

У цьому випадку ми вже маємо сигнал, і ми хочемо генерувати шум, щоб дати нам бажаний SNR.

У той час як шум може прийти в різних смаків в залежності від того, що ви моделювання, гарний початок (особливо для цього радіо , наприклад телескоп) є адитивним білим гауссовским шумом (AWGN) . Як зазначалося в попередніх відповідях, для моделювання AWGN вам потрібно додати нульову середню гауссову випадкову величину до вихідного сигналу. Дисперсія цієї випадкової величини впливатиме на середню потужність шуму.

Для гауссової випадкової величини X середня потужність Еп, також відома як другий момент , дорівнює
[3] Напр

Отже, для білого шуму Напрсередня потужність тоді дорівнює дисперсії Напр.

При моделюванні цього в python, ви можете:
1. Розрахувати дисперсію на основі бажаного SNR та набору існуючих вимірювань, які працювали б, якщо ви очікуєте, що ваші вимірювання мають досить постійні значення амплітуди.
2. Як варіант, ви можете встановити потужність шуму на відомий рівень, щоб відповідати чомусь типу шуму приймача. Шум приймача можна виміряти, спрямувавши телескоп у вільний простір і розрахувавши середню потужність.

У будь-якому випадку, важливо переконатися, що ви додаєте шум до свого сигналу і приймаєте середні значення в лінійному просторі, а не в дБ.

Ось деякий код для генерації сигналу та графіку напруги, потужності у ватах та потужності в дБ:

# Signal Generation
# matplotlib inline

import numpy as np
import matplotlib.pyplot as plt

t = np.linspace(1, 100, 1000)
x_volts = 10*np.sin(t/(2*np.pi))
plt.subplot(3,1,1)
plt.plot(t, x_volts)
plt.title('Signal')
plt.ylabel('Voltage (V)')
plt.xlabel('Time (s)')
plt.show()

x_watts = x_volts ** 2
plt.subplot(3,1,2)
plt.plot(t, x_watts)
plt.title('Signal Power')
plt.ylabel('Power (W)')
plt.xlabel('Time (s)')
plt.show()

x_db = 10 * np.log10(x_watts)
plt.subplot(3,1,3)
plt.plot(t, x_db)
plt.title('Signal Power in dB')
plt.ylabel('Power (dB)')
plt.xlabel('Time (s)')
plt.show()

Сформований сигнал

Ось приклад додавання AWGN на основі бажаного SNR:

# Adding noise using target SNR

# Set a target SNR
target_snr_db = 20
# Calculate signal power and convert to dB 
sig_avg_watts = np.mean(x_watts)
sig_avg_db = 10 * np.log10(sig_avg_watts)
# Calculate noise according to [2] then convert to watts
noise_avg_db = sig_avg_db - target_snr_db
noise_avg_watts = 10 ** (noise_avg_db / 10)
# Generate an sample of white noise
mean_noise = 0
noise_volts = np.random.normal(mean_noise, np.sqrt(noise_avg_watts), len(x_watts))
# Noise up the original signal
y_volts = x_volts + noise_volts

# Plot signal with noise
plt.subplot(2,1,1)
plt.plot(t, y_volts)
plt.title('Signal with noise')
plt.ylabel('Voltage (V)')
plt.xlabel('Time (s)')
plt.show()
# Plot in dB
y_watts = y_volts ** 2
y_db = 10 * np.log10(y_watts)
plt.subplot(2,1,2)
plt.plot(t, 10* np.log10(y_volts**2))
plt.title('Signal with noise (dB)')
plt.ylabel('Power (dB)')
plt.xlabel('Time (s)')
plt.show()

Сигнал із цільовим SNR

І ось приклад для додавання AWGN на основі відомої потужності шуму:

# Adding noise using a target noise power

# Set a target channel noise power to something very noisy
target_noise_db = 10

# Convert to linear Watt units
target_noise_watts = 10 ** (target_noise_db / 10)

# Generate noise samples
mean_noise = 0
noise_volts = np.random.normal(mean_noise, np.sqrt(target_noise_watts), len(x_watts))

# Noise up the original signal (again) and plot
y_volts = x_volts + noise_volts

# Plot signal with noise
plt.subplot(2,1,1)
plt.plot(t, y_volts)
plt.title('Signal with noise')
plt.ylabel('Voltage (V)')
plt.xlabel('Time (s)')
plt.show()
# Plot in dB
y_watts = y_volts ** 2
y_db = 10 * np.log10(y_watts)
plt.subplot(2,1,2)
plt.plot(t, 10* np.log10(y_volts**2))
plt.title('Signal with noise')
plt.ylabel('Power (dB)')
plt.xlabel('Time (s)')
plt.show()

Сигнал із цільовим рівнем шуму


8

Для тих, хто хоче додати шум до багатовимірного набору даних, завантаженого всередині фрейму даних pandas або навіть numpy ndarray, ось приклад:

import pandas as pd
# create a sample dataset with dimension (2,2)
# in your case you need to replace this with 
# clean_signal = pd.read_csv("your_data.csv")   
clean_signal = pd.DataFrame([[1,2],[3,4]], columns=list('AB'), dtype=float) 
print(clean_signal)
"""
print output: 
    A    B
0  1.0  2.0
1  3.0  4.0
"""
import numpy as np 
mu, sigma = 0, 0.1 
# creating a noise with the same dimension as the dataset (2,2) 
noise = np.random.normal(mu, sigma, [2,2]) 
print(noise)

"""
print output: 
array([[-0.11114313,  0.25927152],
       [ 0.06701506, -0.09364186]])
"""
signal = clean_signal + noise
print(signal)
"""
print output: 
          A         B
0  0.888857  2.259272
1  3.067015  3.906358
""" 

1

У реальному житті ви хочете імітувати сигнал білим шумом. Ви повинні додати до свого сигналу випадкові точки, які мають нормальний гауссовий розподіл. Якщо ми говоримо про пристрій, який має чутливість, вказану в одиницях / SQRT (Гц), то вам потрібно розробити стандартне відхилення ваших точок від нього. Тут я даю функцію "white_noise", яка робить це за вас, решта коду є демонстрацією та перевіряє, чи робить вона те, що повинна.

%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
from scipy import signal

"""
parameters: 
rhp - spectral noise density unit/SQRT(Hz)
sr  - sample rate
n   - no of points
mu  - mean value, optional

returns:
n points of noise signal with spectral noise density of rho
"""
def white_noise(rho, sr, n, mu=0):
    sigma = rho * np.sqrt(sr/2)
    noise = np.random.normal(mu, sigma, n)
    return noise

rho = 1 
sr = 1000
n = 1000
period = n/sr
time = np.linspace(0, period, n)
signal_pure = 100*np.sin(2*np.pi*13*time)
noise = white_noise(rho, sr, n)
signal_with_noise = signal_pure + noise

f, psd = signal.periodogram(signal_with_noise, sr)

print("Mean spectral noise density = ",np.sqrt(np.mean(psd[50:])), "arb.u/SQRT(Hz)")

plt.plot(time, signal_with_noise)
plt.plot(time, signal_pure)
plt.xlabel("time (s)")
plt.ylabel("signal (arb.u.)")
plt.show()

plt.semilogy(f[1:], np.sqrt(psd[1:]))
plt.xlabel("frequency (Hz)")
plt.ylabel("psd (arb.u./SQRT(Hz))")
#plt.axvline(13, ls="dashed", color="g")
plt.axhline(rho, ls="dashed", color="r")
plt.show()

Сигнал із шумом

PSD


0

Чудові відповіді вище. Нещодавно у мене була необхідність генерувати змодельовані дані, і це те, що я почав використовувати. Спільний доступ до справи, корисний і для інших,

import logging
__name__ = "DataSimulator"
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

import numpy as np
import pandas as pd

def generate_simulated_data(add_anomalies:bool=True, random_state:int=42):
    rnd_state = np.random.RandomState(random_state)
    time = np.linspace(0, 200, num=2000)
    pure = 20*np.sin(time/(2*np.pi))

    # concatenate on the second axis; this will allow us to mix different data 
    # distribution
    data = np.c_[pure]
    mu = np.mean(data)
    sd = np.std(data)
    logger.info(f"Data shape : {data.shape}. mu: {mu} with sd: {sd}")
    data_df = pd.DataFrame(data, columns=['Value'])
    data_df['Index'] = data_df.index.values

    # Adding gaussian jitter
    jitter = 0.3*rnd_state.normal(mu, sd, size=data_df.shape[0])
    data_df['with_jitter'] = data_df['Value'] + jitter

    index_further_away = None
    if add_anomalies:
        # As per the 68-95-99.7 rule(also known as the empirical rule) mu+-2*sd 
        # covers 95.4% of the dataset.
        # Since, anomalies are considered to be rare and typically within the 
        # 5-10% of the data; this filtering
        # technique might work 
        #for us(https://en.wikipedia.org/wiki/68%E2%80%9395%E2%80%9399.7_rule)
        indexes_furhter_away = np.where(np.abs(data_df['with_jitter']) > (mu + 
         2*sd))[0]
        logger.info(f"Number of points further away : 
        {len(indexes_furhter_away)}. Indexes: {indexes_furhter_away}")
        # Generate a point uniformly and embed it into the dataset
        random = rnd_state.uniform(0, 5, 1)
        data_df.loc[indexes_furhter_away, 'with_jitter'] +=  
        random*data_df.loc[indexes_furhter_away, 'with_jitter']
    return data_df, indexes_furhter_away

0

AWGN Подібно до функції Matlab

def awgn(sinal):
    regsnr=54
    sigpower=sum([math.pow(abs(sinal[i]),2) for i in range(len(sinal))])
    sigpower=sigpower/len(sinal)
    noisepower=sigpower/(math.pow(10,regsnr/10))
    noise=math.sqrt(noisepower)*(np.random.uniform(-1,1,size=len(sinal)))
    return noise
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.