aggiungendo rumore a un segnale in python


95

Voglio aggiungere un po 'di rumore casuale a un segnale a 100 bin che sto simulando in Python - per renderlo più realistico.

A livello di base, il mio primo pensiero è stato quello di andare bin per bin e semplicemente generare un numero casuale tra un certo intervallo e sommarlo o sottrarlo dal segnale.

Speravo (dato che questo è python) che ci potesse essere un modo più intelligente per farlo tramite numpy o qualcosa del genere. (Suppongo che idealmente un numero estratto da una distribuzione gaussiana e aggiunto a ogni bin sarebbe anche meglio.)

Grazie in anticipo di eventuali risposte.


Sono solo nella fase di pianificazione del mio codice, quindi non ho nulla da mostrare. Stavo solo pensando che potrebbe esserci un modo più sofisticato di generare il rumore.

In termini di output, se avessi 10 bin con i seguenti valori:

Raccoglitore 1: 1 Raccoglitore 2: 4 Raccoglitore 3: 9 Raccoglitore 4:16 Raccoglitore 5:25 Raccoglitore 6:25 Raccoglitore 7:16 Raccoglitore 8: 9 Raccoglitore 9: 4 Raccoglitore 10: 1

Mi chiedevo solo se ci fosse una funzione predefinita che potesse aggiungere rumore per darmi qualcosa come:

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

In caso contrario, andrò semplicemente bin-by-bin e aggiungerò un numero selezionato da una distribuzione gaussiana a ciascuno di essi.

Grazie.


In realtà è un segnale da un radiotelescopio che sto simulando. Voglio essere in grado di scegliere eventualmente il rapporto segnale / rumore della mia simulazione.


2
Dimostra il codice che hai provato o un problema specifico che stai riscontrando. Anche gli input di esempio e l'output desiderato potrebbero fare molto.
gddc

2
Che tipo di segnale è? Che tipo di rumore vuoi introdurre? "Realistico" dipende dal tipo di segnale. Il rumore audio non è lo stesso del rumore dell'immagine, ad esempio.
Diego Basch

Risposte:


125

Puoi generare un array di rumore e aggiungerlo al tuo segnale

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
In alcuni contesti, potrebbe avere più senso moltiplicare il segnale per un array di rumore (centrato intorno a 1), piuttosto che aggiungere un array di rumore, ma ovviamente ciò dipende dalla natura del rumore che stai cercando di simulare.
Edward Loper

69

... E per coloro che, come me, sono all'inizio della loro curva di apprendimento insensibile,

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

57

Per coloro che cercano di stabilire la connessione tra SNR e una normale variabile casuale generata da numpy:

[1] Rapporto SNR, dove è importante tenere presente che P è la potenza media .

Oppure in dB:
[2]SNR dB2

In questo caso, abbiamo già un segnale e vogliamo generare rumore per darci un SNR desiderato.

Mentre il rumore può avere diversi gusti a seconda di ciò che stai modellando, un buon inizio (specialmente per questo esempio di radiotelescopio) è Additive White Gaussian Noise (AWGN) . Come affermato nelle risposte precedenti, per modellare AWGN è necessario aggiungere una variabile casuale gaussiana a media zero al segnale originale. La varianza di quella variabile casuale influenzerà la potenza media del rumore.

Per una variabile casuale gaussiana X, la potenza media Ep, nota anche come secondo momento , è
[3] Ex

Quindi per il rumore bianco, Exe la potenza media è quindi uguale alla varianza Ex.

Quando si modella questo in python, è possibile
1. Calcolare la varianza in base a un SNR desiderato e un insieme di misurazioni esistenti, che funzionerebbero se si prevede che le misurazioni abbiano valori di ampiezza abbastanza coerenti.
2. In alternativa, è possibile impostare la potenza del rumore a un livello noto in modo che corrisponda a qualcosa come il rumore del ricevitore. Il rumore del ricevitore può essere misurato puntando il telescopio nello spazio libero e calcolando la potenza media.

In ogni caso, è importante assicurarsi di aggiungere rumore al segnale e prendere le medie nello spazio lineare e non in unità dB.

Ecco un codice per generare un segnale e tracciare la tensione, la potenza in Watt e la potenza in dB:

# 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()

Segnale generato

Ecco un esempio per l'aggiunta di AWGN in base a un SNR desiderato:

# 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()

Segnale con SNR target

Ed ecco un esempio per l'aggiunta di AWGN in base a una potenza di rumore nota:

# 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()

Segnale con livello di rumore target


8

Per coloro che desiderano aggiungere rumore a un set di dati multidimensionale caricato all'interno di un dataframe panda o anche un numpy ndarray, ecco un esempio:

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

Nella vita reale desideri simulare un segnale con rumore bianco. Dovresti aggiungere al tuo segnale punti casuali che hanno una distribuzione gaussiana normale. Se parliamo di un dispositivo che ha la sensibilità data in unità / SQRT (Hz), allora devi escogitare la deviazione standard dei tuoi punti da esso. Qui do la funzione "white_noise" che fa questo per te, e il resto del codice è dimostrativo e controlla se fa quello che dovrebbe.

%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()

Segnale con rumore

PSD


0

Risposte fantastiche sopra. Recentemente ho avuto la necessità di generare dati simulati e questo è ciò che sono arrivato a utilizzare. Condividere in caso utile anche ad altri,

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 Simile alla funzione 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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.