agregar ruido a una señal en Python

95

Quiero agregar algo de ruido aleatorio a una señal de 100 contenedores que estoy simulando en Python, para que sea más realista.

En un nivel básico, mi primer pensamiento fue ir bin por bin y simplemente generar un número aleatorio entre un cierto rango y sumar o restar esto de la señal.

Tenía la esperanza (ya que esto es Python) que podría haber una forma más inteligente de hacer esto a través de numpy o algo así. (Supongo que idealmente también sería mejor un número extraído de una distribución gaussiana y agregado a cada contenedor).

Gracias de antemano por cualquier respuesta.


Estoy en la etapa de planificación de mi código, por lo que no tengo nada que mostrar. Estaba pensando que podría haber una forma más sofisticada de generar ruido.

En términos de salida, si tuviera 10 contenedores con los siguientes valores:

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

Me preguntaba si había una función predefinida que pudiera agregar ruido para darme algo como:

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

Si no, simplemente iré bin por bin y agregaré un número seleccionado de una distribución gaussiana a cada uno.

Gracias.


En realidad, es una señal de un radiotelescopio que estoy simulando. Quiero poder elegir eventualmente la relación señal / ruido de mi simulación.

usuario1551817
fuente
2
Demuestre el código que ha probado o un problema específico que está encontrando. Las entradas de muestra y la salida deseada también serían de gran ayuda.
gddc
2
¿Qué tipo de señal es? ¿Qué tipo de ruido quieres introducir? "Realista" depende del tipo de señal. El ruido de audio no es lo mismo que el ruido de la imagen, por ejemplo.
Diego Basch

Respuestas:

125

Puede generar una matriz de ruido y agregarla a su señal

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
Akavall
fuente
17
En algunos contextos, podría tener más sentido multiplicar su señal por una matriz de ruido (centrada alrededor de 1), en lugar de agregar una matriz de ruido, pero por supuesto eso depende de la naturaleza del ruido que está tratando de simular.
Edward Loper
69

... Y para aquellos que, como yo, están muy temprano en su curva de aprendizaje,

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

Para aquellos que intentan hacer la conexión entre SNR y una variable aleatoria normal generada por numpy:

[1] Relación SNR, donde es importante tener en cuenta que P es la potencia promedio .

O en dB:
[2]SNR dB2

En este caso, ya tenemos una señal y queremos generar ruido para darnos una SNR deseada.

Si bien el ruido puede tener diferentes sabores dependiendo de lo que esté modelando, un buen comienzo (especialmente para este ejemplo de radiotelescopio) es el ruido blanco gaussiano aditivo (AWGN) . Como se indicó en las respuestas anteriores, para modelar AWGN debe agregar una variable aleatoria gaussiana de media cero a su señal original. La varianza de esa variable aleatoria afectará la potencia de ruido promedio .

Para una variable aleatoria gaussiana X, la potencia promedio Ep, también conocida como segundo momento , es
[3] Ex

Entonces, para el ruido blanco, Exla potencia promedio es igual a la varianza Ex.

Al modelar esto en Python, puede:
1. Calcular la varianza en función de una SNR deseada y un conjunto de medidas existentes, lo que funcionaría si espera que sus medidas tengan valores de amplitud bastante consistentes.
2. Alternativamente, puede establecer la potencia de ruido a un nivel conocido para igualar algo como el ruido del receptor. El ruido del receptor se puede medir apuntando el telescopio hacia el espacio libre y calculando la potencia promedio.

De cualquier manera, es importante asegurarse de agregar ruido a su señal y tomar promedios en el espacio lineal y no en unidades de dB.

Aquí hay un código para generar una señal y trazar el voltaje, la potencia en vatios y la potencia en 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()

Señal generada

A continuación, se muestra un ejemplo para agregar AWGN en función de una SNR deseada:

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

Señal con SNR objetivo

Y aquí hay un ejemplo para agregar AWGN basado en una potencia de ruido conocida:

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

Señal con nivel de ruido objetivo

tmcdevitt
fuente
8

Para aquellos que quieran agregar ruido a un conjunto de datos multidimensional cargado dentro de un marco de datos pandas o incluso un ndarray numpy, aquí hay un ejemplo:

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
""" 
Mohamed Ali JAMAOUI
fuente
1

En la vida real, desea simular una señal con ruido blanco. Debe agregar a su señal puntos aleatorios que tengan una distribución gaussiana normal. Si hablamos de un dispositivo que tiene una sensibilidad dada en unidad / SQRT (Hz), entonces necesita idear una desviación estándar de sus puntos. Aquí le doy la función "white_noise" que hace esto por usted, y el resto del código es una demostración y verifico si hace lo que debería.

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

Señal con ruido

PSD

drgrujic
fuente
0

Respuestas impresionantes arriba. Recientemente tuve la necesidad de generar datos simulados y esto es lo que terminé usando. Compartir en caso de que sea útil para otros también,

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
Pramit
fuente
0

AWGN similar a la función 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
Neitzke
fuente