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.
Respuestas:
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
fuente
... 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
fuente
Para aquellos que intentan hacer la conexión entre SNR y una variable aleatoria normal generada por numpy:
[1] , donde es importante tener en cuenta que P es la potencia promedio .
O en dB:
[2]
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 , también conocida como segundo momento , es
[3]
Entonces, para el ruido blanco, la potencia promedio es igual a la varianza .
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()
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()
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()
fuente
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 """
fuente
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()
fuente
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
fuente
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
fuente