python - simbolo - ¿Cómo generaliza la creación de una lista con muchas variables y condiciones de ''if''?
python simbolo (4)
Creo una lista de la siguiente manera:
[''v0'' if x%4==0 else ''v1'' if x%4==1 else ''v2'' if x%4==2 else ''v3'' for x in list_1]
¿Cómo generalizar la creación de una lista de este tipo, de modo que pueda expandirse fácilmente por un mayor número de variables y condiciones posteriores?
Formato de cadena
¿Por qué no usar una operación de módulo aquí, y hacer un formato de cadena, como:
[''v{}''.format(x%4) for x in list_1]
Aquí, por lo tanto, calculamos x%4
, y agregamos esto a ''v
'' en la cadena. Lo bueno es que podemos cambiar fácilmente 4
, a otro número.
Tupla o lista de indexación
En caso de que la cadena de salida no siga dicha estructura, podemos construir una lista o tupla para contener los valores. Me gusta:
# in case the values do not follow a certain structure
vals = (''v0'', ''v1'', ''v2'', ''v3'')
[vals[x%4] for x in list_1]
Al indexarlo de esa manera, queda claro qué valores se asignarán en qué índice. Esto funciona bien, dado el resultado de la operación, aquí x%4
, se asigna a una ℕ n (con una n pequeña razonable).
Diccionario (predeterminado)
En caso de que la operación no se asigne en un ℕ n , pero aún en un número finito de elementos hashable , podemos usar un diccionario. Por ejemplo:
d = {0: ''v0'', 1: ''v1'', 2: ''v2'', 3: ''v3''}
o en caso de que queramos un valor de "reserva", un valor que se utiliza dado que la búsqueda falla:
from collections import defaultdict
d = defaultdict(lambda: 1234, {0: ''v0'', 1: ''v1'', 2: ''v2'', 3: ''v3''})
donde aquí se usa 1234
como valor de reserva, y luego podemos usar:
[d[x%4] for x in list_1]
Usar d[x%4]
sobre d.get(x%4)
dado que d
es un diccionario puede ser más útil si queremos evitar que las búsquedas que fallan pasen inadvertidas. Será en ese caso error. Aunque los errores generalmente no son una buena señal, puede ser mejor provocar un error en caso de que la búsqueda falle en lugar de agregar un valor predeterminado, ya que puede ser un síntoma de que algo no está funcionando correctamente.
Aquí están mis intentos de una solución genérica . Primero, la configuración -
list_1 = [1, 2, 4, 5, 10, 4, 3]
Las dos primeras opciones están basadas en python puro, mientras que las dos últimas usan bibliotecas numéricas (numpy y pandas).
dict
get
Generar un mapping
de claves a valores. En la lista de comprensión, consulta dict.get
-
mapping = {0 : ''v0'', 1 : ''v1'', 2 : ''v2''}
r = [mapping.get(x % 4, ''v3'') for x in list_1]
r
[''v1'', ''v2'', ''v0'', ''v1'', ''v2'', ''v0'', ''v3'']
Aquí, ''v3''
es el valor predeterminado que se devuelve cuando el resultado de x % 4
no existe como una clave en la mapping
.
Esto funcionaría para cualquier conjunto arbitrario de condiciones y valores, no solo la condición descrita en la pregunta (módulo aritmético).
collections
. defaultdict
Una solución similar sería posible usando un defaultdict
:
from collections import defaultdict
mapping = defaultdict(lambda: ''v3'', {0: ''v0'', 1: ''v1'', 2: ''v2'', 3: ''v3''})
r = [mapping[x % 4] for x in list_1]
r
[''v1'', ''v2'', ''v0'', ''v1'', ''v2'', ''v0'', ''v3'']
Esto funciona de manera similar a la Opción 1 .
numpy
char
. add
Si usa numpy
, entonces podría estar interesado en una solución vectorizada que implique modulación aritmética y adición de emisión:
r = np.char.add(''v'', (np.array(list_1) % 4).astype(''<U8''))
r
array([''v1'', ''v2'', ''v0'', ''v1'', ''v2'', ''v0'', ''v3''],
dtype=''<U9'')
Si necesita una lista como resultado final, puede llamar a r.tolist()
. Tenga en cuenta que esta solución está optimizada para su caso de uso particular. Un enfoque más genérico se lograría con numpy
usando np.where
/ np.select
.
pd
Series
mod
+ pd
. Series
radd
Una solución similar también funcionaría con pandas
mod
+ radd
-
r = pd.Series(list_1).mod(4).astype(str).radd(''v'')
r
0 v1
1 v2
2 v0
3 v1
4 v2
5 v0
6 v3
dtype: object
r.tolist()
[''v1'', ''v2'', ''v0'', ''v1'', ''v2'', ''v0'', ''v3'']
En el ejemplo dado, está claro que podemos "comprimir" las condiciones, lo que lleva a las soluciones específicas que se dieron aquí. Sin embargo, en el caso general, no podemos asumir que hay algún "truco" para escribir rápidamente todas las condiciones posibles en una sola línea.
Escribiría todas las condiciones en una función:
def conditions(x):
if x == <option a>:
return <result a>
elif x == <option b>:
return <result b>
.
.
.
else:
return <default option>
Si solo estás usando operaciones de comparación, puedes usar un collections.defaultdict
, como se muestra en las otras respuestas. Si las condiciones son más complejas, es probable que tenga que escribir toda la función como se muestra.
Ahora para tu lista de comprensión, solo puedes hacer:
values = [conditions(x) for x in my_list_of_values]
def condition(rule, out):
return lambda x: out(x) if rule(x) else None
def rule1(x): return x%4 == 0
def out1(x): return ''v0''
def rule2(x): return x%4 == 1
def out2(x): return ''v1''
def rule3(x): return x%4 == 2
def out3(x): return ''v2''
lastrule = lambda x: True
lastout = lambda x: ''v3''
check1 = condition(rule1, out1)
check2 = condition(rule2, out2)
check3 = condition(rule3, out3)
check_last = condition(lastrule, lastout)
def tranform(*check_list):
def trans_value(x):
for trans in check_list:
if trans(x) is not None:
return trans(x)
return trans_value
list_1=[4,5,6,7,8]
print([tranform(check1, check2, check3, check_last)(x) for x in list_1])
Para verificaciones largas, puede ser más fácil formar primero una lista de condiciones. Suponga que la fórmula condicional y la salida son funciones de x, no hay otros parámetros de entrada. La forma a continuación guarda algo de escritura pero mantiene la aditividad para verificaciones largas.
Para lograr un método aún más genérico (condiciones más complejas, múltiples parámetros), algunos procedimientos compuestos podrían ser útiles (algo así como both(greater, either(smaller, identity))
y todo el programa necesita ser reestructurado nuevamente, lo que significa la aditividad de El programa aún no es ideal, ya que todavía no es lo suficientemente genérico.
outconstants = [''v0'', ''v1'', ''v2'', ''v3'']
# for this specific example. In general, only outf is needed (see below)
n = len(outconstant)
outf = lambda out: lambda x: out
outs = [outf(out) for out in outconstants]
# define your own outf formula, if not output constant
# define multiple formulas and put into list, if different type of outputs are needed
rights = map(lambda constant: lambda x: constant, range(n-1))
lefts = [lambda x: x%4 for _ in range(n-1)]
# right and left formulas can be also defined separately and then put into list
def identity(a, b): return lambda x: a(x) == b(x)
# define other rules if needed and form them into rules list with proper orders
# e.g., def greater(a, b): return lambda x: a(x) > b(x), ...
lastrule=lambda x: True
rules = list(map(identity, lefts, rights))
rules.append(lastrule)
# in complex case, each unique rule needs to be defined separately and put into list
# if new rule is needed, define it and append here before lastrule (additive)
def transform(rules, outs):
def trans_value(x):
for rule, out in zip(rules, outs):
if rule(x):
return out(x)
return trans_value
list_1=[4,5,6,7,8]
print([transform(rules, outs)(x) for x in list_1])