Me gustaría obtener los rangos donde los valores no son Ninguno en una lista, por ejemplo:
test1 = [None, 0, None] test2 = [2,1,None] test3 = [None,None,3] test4 = [1,0,None,0,0,None,None,1,None,0] res1 = [[1,1]] res2 = [[0,1]] res3 = [[2,2]] res4 = [[0,1],[3,4],[7,7],[9,9]]Esta es mi implementación súper larga, que no funciona perfectamente...
def get_not_None_ranges(list_): # Example [0, 2, None, 1, 4] -> [[0, 1], [3, 4]] r = [] end_i = len(list_)-1 if list_[0] == None: s = None else: s = 0 for i, elem in enumerate(list_): if s != None: if elem == None and end_i != i: r.append([s,i-1]) s = i+1 if end_i == i: if s > i: r=r elif s==i and elem == None: r=r else: r.append([s,i]) else: if elem != None: s = i if end_i == i: if s > i: r=r else: r.append([s,i]) return rComo puede ver, los resultados a veces son incorrectos:
print(get_not_None_ranges(test1)) print(get_not_None_ranges(test2)) print(get_not_None_ranges(test3)) print(get_not_None_ranges(test4)) [[1, 2]] [[0, 2]] [[2, 2]] [[0, 1], [3, 4], [6, 5], [7, 7], [9, 9]]Entonces, me preguntaba si ustedes conocen una forma mucho mejor de lograr esto.
Solo necesita iterar sobre la lista y verificar dos condiciones:
None y el elemento actual no es None , comience un nuevo "rango".None y el elemento actual es None , finaliza el rango actualmente activo en el índice anterior. def gnnr(lst): all_ranges = [] current_range = [] prev_item = None for index, item in enumerate(lst): # Condition 1 if prev_item is None and item is not None: current_range.append(index) # Condition 2 elif prev_item is not None and item is None: current_range.append(index - 1) # Close current range at the previous index all_ranges.append(current_range) # Add to all_ranges current_range = [] # Reset current_range prev_item = item # If current_range isn't closed, close it at the last index of the list if current_range: current_range.append(index) all_ranges.append(current_range) return all_rangesLlamar a esta función con sus casos de prueba da el resultado esperado:
[[1, 1]] [[0, 1]] [[2, 2]] [[0, 1], [3, 4], [7, 7], [9, 9]]Darle una oportunidad. El código usa Type Hint y una tupla con nombre para aumentar la legibilidad.
from typing import NamedTuple,List,Any class Range(NamedTuple): left: int right: int def get_ranges(lst: List[Any]) -> List[Range]: ranges : List[Range] = [] left = None right = None for i,x in enumerate(lst): is_none = x is None if is_none: if left is not None : right = right if right is not None else left ranges.append(Range(left,right)) left = None right = None else: if left is None: left = i else: right = i if left is not None: right = right if right is not None else left ranges.append(Range(left,right)) return ranges data = [[1,0,None,0,0,None,None,1,None,0],[None,None,3],[2,1,None],[None, 0, None]] for entry in data: print(get_ranges(entry))salida
[Range(left=0, right=1), Range(left=3, right=4), Range(left=7, right=7), Range(left=9, right=9)] [Range(left=2, right=2)] [Range(left=0, right=1)] [Range(left=1, right=1)]Utilice itertools.groupby :
from itertools import groupby test1 = [None, 0, None] test2 = [2, 1, None] test3 = [None, None, 3] test4 = [1, 0, None, 0, 0, None, None, 1, None, 0] def get_not_None_ranges(lst): result = [] for key, group in groupby(enumerate(lst), key=lambda x: x[1] is not None): if key: index, _ = next(group) result.append([index, index + sum(1 for _ in group)]) return result print(get_not_None_ranges(test1)) print(get_not_None_ranges(test2)) print(get_not_None_ranges(test3)) print(get_not_None_ranges(test4))Producción
[[1, 1]] [[0, 1]] [[2, 2]] [[0, 1], [3, 4], [7, 7], [9, 9]]Bueno, podemos resolver esto usando el enfoque clásico de ventana deslizante.
Aquí está la solución que funciona bien:
def getRanges(nums): left = right = 0 ranges, n = [], len(nums) while right < n: while left < n and nums[left] == None: left += 1 right += 1 while right < n and nums[right] != None: right += 1 if right >= n: break ranges.append([left, right - 1]) left = right = right + 1 return ranges + [[left, right - 1]] if right - 1 >= left else rangesVamos a probarlo:
test = [ [1, 0, None, 0, 0, None, None, 1, None, 0], [None, None, 3], [2, 1, None], [None, 0, None], ] for i in test: print(getRanges(i))Producción:
[[0, 1], [3, 4], [7, 7], [9, 9]] [[2, 2]] [[0, 1]] [[1, 1]]Qué tal si:-
test1 = [None, 0, None] test2 = [2, 1, None] test3 = [None, None, 3] test4 = [1, 0, None, 0, 0, None, None, 1, None, 0] def goal(L): r = [] _r = None for i, e in enumerate(L): if e is not None: if _r: _r[1] = i else: _r = [i, i] else: if _r: r.append(_r) _r = None if _r: r.append(_r) return r for _l in [test1, test2, test3, test4]: print(goal(_l))Usando el primero y el último de cada grupo de no ninguno:
from itertools import groupby def get_not_None_ranges(lst): result = [] for nones, group in groupby(enumerate(lst), lambda x: x[1] is None): if not nones: first = last = next(group) for last in group: pass result.append([first[0], last[0]]) return resultAquí está mi ejemplo. Definitivamente NO es la forma más eficiente, pero creo que es más intuitiva y puedes optimizarla más adelante.
def get_not_None_ranges(list_: list): res = [] start_index = -1 for i in range(len(list_)): e = list_[i] if e is not None: if start_index < 0: start_index = i else: if start_index >= 0: res.append([start_index, i - 1]) start_index = -1 if start_index >= 0: res.append([start_index, len(list_) - 1]) return resEl pensamiento principal de esta función:
Creo que puede ser un poco complejo, puede ayudar pegar el código anterior y depurarlo línea por línea en un depurador.
Una solución no groupby que no necesita un tratamiento adicional para el último grupo:
def get_not_None_ranges(lst): result = [] it = enumerate(lst) for i, x in it: if x is not None: first = last = i for i, x in it: if x is None: break last = i result.append([first, last]) return resultCada vez que encuentro el primero de una racha que no es Ninguno, uso un bucle interno para ejecutar de inmediato hasta el último de esa racha. Para permitir que ambos bucles usen el mismo iterador, lo almaceno en una variable.
Otra solución (una sola línea con itertools.groupby ):
from itertools import groupby out = [[(v := list(g))[0][1], v[-1][1]] for _, g in groupby(enumerate(i for i, v in enumerate(testX) if not v is None), lambda k: k[0] - k[1],)]Pruebas:
test1 = [None, 0, None] test2 = [2, 1, None] test3 = [None, None, 3] test4 = [1, 0, None, 0, 0, None, None, 1, None, 0] tests = [test1, test2, test3, test4] for t in tests: out = [ [(v := list(g))[0][1], v[-1][1]] for _, g in groupby( enumerate(i for i, v in enumerate(t) if not v is None), lambda k: k[0] - k[1], ) ] print(out)Huellas dactilares:
[[1, 1]] [[0, 1]] [[2, 2]] [[0, 1], [3, 4], [7, 7], [9, 9]]