matrices - print matrix python
Encuentre un gran número de valores consecutivos que cumplan la condición en una matriz numpy (7)
@ joe-kington Tengo una mejora de velocidad de aproximadamente 20% -25% sobre la solución np.diff / np.nonzero
utilizando argmax
lugar (consulte el código siguiente, la condition
es booleana)
def contiguous_regions(condition):
idx = []
i = 0
while i < len(condition):
x1 = i + condition[i:].argmax()
try:
x2 = x1 + condition[x1:].argmin()
except:
x2 = x1 + 1
if x1 == x2:
if condition[x1] == True:
x2 = len(condition)
else:
break
idx.append( [x1,x2] )
i = x2
return idx
Por supuesto, su kilometraje puede variar dependiendo de sus datos.
Además, no estoy del todo seguro, pero supongo que numpy puede optimizar argmin/argmax
sobre matrices booleanas para detener la búsqueda en la primera aparición de True/False
. Eso podría explicarlo.
Tengo algunos datos de audio cargados en una matriz numpy y deseo segmentar los datos encontrando partes silenciosas, es decir, partes donde la amplitud de audio está por debajo de un cierto umbral durante un período de tiempo.
Una forma extremadamente simple de hacer esto es algo como esto:
values = ''''.join(("1" if (abs(x) < SILENCE_THRESHOLD) else "0" for x in samples))
pattern = re.compile(''1{%d,}''%int(MIN_SILENCE))
for match in pattern.finditer(values):
# code goes here
El código anterior encuentra partes donde hay al menos MIN_SILENCE elementos consecutivos más pequeños que SILENCE_THRESHOLD.
Ahora, obviamente, el código anterior es horriblemente ineficiente y un terrible abuso de las expresiones regulares. ¿Hay algún otro método que sea más eficiente, pero que resulte en un código igualmente simple y corto?
Aquí hay una solución basada en números.
Creo que (?) Debería ser más rápido que las otras opciones. Esperemos que sea bastante claro.
Sin embargo, requiere el doble de memoria que las diversas soluciones basadas en generadores. Siempre que pueda mantener una sola copia temporal de sus datos en la memoria (para el diff), y una matriz booleana de la misma longitud que sus datos (1 bit por elemento), debería ser bastante eficiente ...
import numpy as np
def main():
# Generate some random data
x = np.cumsum(np.random.random(1000) - 0.5)
condition = np.abs(x) < 1
# Print the start and stop indicies of each region where the absolute
# values of x are below 1, and the min and max of each of these regions
for start, stop in contiguous_regions(condition):
segment = x[start:stop]
print start, stop
print segment.min(), segment.max()
def contiguous_regions(condition):
"""Finds contiguous True regions of the boolean array "condition". Returns
a 2D array where the first column is the start index of the region and the
second column is the end index."""
# Find the indicies of changes in "condition"
d = np.diff(condition)
idx, = d.nonzero()
# We need to start things after the change in "condition". Therefore,
# we''ll shift the index by 1 to the right.
idx += 1
if condition[0]:
# If the start of condition is True prepend a 0
idx = np.r_[0, idx]
if condition[-1]:
# If the end of condition is True, append the length of the array
idx = np.r_[idx, condition.size] # Edit
# Reshape the result into two columns
idx.shape = (-1,2)
return idx
main()
Esto debería devolver una lista de pares (start,length)
:
def silent_segs(samples,threshold,min_dur):
start = -1
silent_segments = []
for idx,x in enumerate(samples):
if start < 0 and abs(x) < threshold:
start = idx
elif start >= 0 and abs(x) >= threshold:
dur = idx-start
if dur >= min_dur:
silent_segments.append((start,dur))
start = -1
return silent_segments
Y una prueba sencilla:
>>> s = [-1,0,0,0,-1,10,-10,1,2,1,0,0,0,-1,-10]
>>> silent_segs(s,2,2)
[(0, 5), (9, 5)]
Hay una solución muy conveniente para esto usando scipy.ndimage
. Para una matriz:
a = array([1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0])
que puede ser el resultado de una condición aplicada a otra matriz, encontrar las regiones contiguas es tan simple como:
regions = scipy.ndimage.find_objects(scipy.ndimage.label(a)[0])
Luego, se puede aplicar cualquier función a esas regiones, por ejemplo, como:
[np.sum(a[r]) for r in regions]
Ligeramente descuidado, pero simple y rápido, si no te importa usar scipy:
from scipy.ndimage import gaussian_filter
sigma = 3
threshold = 1
above_threshold = gaussian_filter(data, sigma=sigma) > threshold
La idea es que las partes silenciosas de los datos se suavizarán a una amplitud baja, y las regiones ruidosas no. Sintonice ''sigma'' para afectar la duración de una región ''tranquila''; sintonice el ''umbral'' para afectar lo silencioso que debe ser. Esto se ralentiza para sigma grande, en cuyo momento el uso de suavizado basado en FFT podría ser más rápido.
Esto tiene la ventaja añadida de que los "píxeles calientes" individuales no interrumpirán su búsqueda de silencio, por lo que es un poco menos sensible a ciertos tipos de ruido.
No he probado esto, pero usted debería estar cerca de lo que está buscando. Un poco más de líneas de código, pero debería ser más eficiente, legible y no abusar de las expresiones regulares :-)
def find_silent(samples):
num_silent = 0
start = 0
for index in range(0, len(samples)):
if abs(samples[index]) < SILENCE_THRESHOLD:
if num_silent == 0:
start = index
num_silent += 1
else:
if num_silent > MIN_SILENCE:
yield samples[start:index]
num_silent = 0
if num_silent > MIN_SILENCE:
yield samples[start:]
for match in find_silent(samples):
# code goes here
otra forma de hacerlo de forma rápida y concisa:
import pylab as pl
v=[0,0,1,1,0,0,1,1,1,1,1,0,1,0,1,1,0,0,0,0,0,1,0,0]
vd = pl.diff(v)
#vd[i]==1 for 0->1 crossing; vd[i]==-1 for 1->0 crossing
#need to add +1 to indexes as pl.diff shifts to left by 1
i1=pl.array([i for i in xrange(len(vd)) if vd[i]==1])+1
i2=pl.array([i for i in xrange(len(vd)) if vd[i]==-1])+1
#corner cases for the first and the last element
if v[0]==1:
i1=pl.hstack((0,i1))
if v[-1]==1:
i2=pl.hstack((i2,len(v)))
ahora i1 contiene el índice inicial y i2 el índice final de 1, ..., 1 áreas