Supongamos que tengo un df que tiene columnas de 'ID', 'col_1', 'col_2'. Y yo definir una función :

f = lambda x, y : my_function_expression.

Ahora quiero solicitar la f a df‘s dos columnas 'col_1', 'col_2' a elemento-sabio calcular una nueva columna 'col_3' , algo así como :

df['col_3'] = df[['col_1','col_2']].apply(f)  
# Pandas gives : TypeError: ('<lambda>() takes exactly 2 arguments (1 given)'

Cómo hacerlo ?

** Añadir el detalle de la muestra de la siguiente ***

import pandas as pd

df = pd.DataFrame({'ID':['1','2','3'], 'col_1': [0,2,3], 'col_2':[1,4,5]})
mylist = ['a','b','c','d','e','f']

def get_sublist(sta,end):
    return mylist[sta:end+1]

#df['col_3'] = df[['col_1','col_2']].apply(get_sublist,axis=1)
# expect above to output df as below 

  ID  col_1  col_2            col_3
0  1      0      1       ['a', 'b']
1  2      2      4  ['c', 'd', 'e']
2  3      3      5  ['d', 'e', 'f']
  • puede aplicar f directamente a las columnas: df[‘col_3’] = f(df[‘col_1’],df[‘col_2’])
  • sería útil saber qué f está haciendo
  • no, df[‘col_3’] = f(df[‘col_1’],df[‘col_2’]) no funciona. Para f sólo acepta escalar de entrada , no del vector de entradas. OK, se puede asumir que f = lambda x,y : x+y . (por supuesto, mi real f no es así de simple, de lo contrario, puede directamente df[‘col_3’] = df[‘col_1’] + df[‘col_2’] )
  • He encontrado una relacionada con Q&a en el siguiente url, pero mi problema es que el cálculo de una nueva columna, dos columnas existentes, no 2 en 1 . stackoverflow.com/questions/12356501/…
  • Creo que mi respuesta stackoverflow.com/a/52854800/5447172 responde en la mayoría de los Python / Pandanic manera, con ninguna de las soluciones numéricas o de indexación. Se produce exactamente el resultado que se requiere en el ejemplo.
  • Me sugirió un map método para hacer esto: github.com/pandas-dev/pandas/issues/20807 Siéntase libre de pedir o proponer un parche.

InformationsquelleAutor bigbug | 2012-11-11

12 Comentarios

  1. 249

    He aquí un ejemplo de uso de apply en el dataframe, que yo estoy llamando con axis = 1.

    Nota la diferencia es que en lugar de tratar de pasar dos valores a la función f, vuelva a escribir la función que acepta un pandas Serie de objetos y, a continuación, el índice de la Serie para obtener los valores necesarios.

    In [49]: df
    Out[49]: 
              0         1
    0  1.000000  0.000000
    1 -0.494375  0.570994
    2  1.000000  0.000000
    3  1.876360 -0.229738
    4  1.000000  0.000000
    
    In [50]: def f(x):    
       ....:  return x[0] + x[1]  
       ....:  
    
    In [51]: df.apply(f, axis=1) #passes a Series object, row-wise
    Out[51]: 
    0    1.000000
    1    0.076619
    2    1.000000
    3    1.646622
    4    1.000000

    Dependiendo de su caso de uso, a veces es útil para crear un pandas group objeto y, a continuación, utilizar apply en el grupo.

    • Sí, traté de uso se aplican, pero no puede encontrar la sintaxis válida de expresión. Y si cada fila de la df es único, todavía uso groupby?
    • Se añadió un ejemplo a mi respuesta, espero que esto no lo que estás buscando. Si no, favor de proporcionar un ejemplo específico de la función desde sum es resuelto con éxito por cualquiera de los métodos sugeridos hasta ahora.
    • me ofrecen un detalle de la muestra en cuestión. Cómo utilizar los Pandas ‘aplicar’ función para crear ‘col_3’ ?
    • Mi respuesta es aplicar un cable (jaja) para el ejemplo que has añadido a tu pregunta. Uso aplicar sobre la totalidad del dataframe, pasando en filas con df.aplicar(f, eje=1). A continuación, vuelva a escribir la función get_sublist(x) al índice de la col valores como start_idx = x[1], end_idx = x[2].
    • Le pls pegar el código ? Reescribir la función: def get_sublist(x): return milista[x[1]:x[2] + 1] y en el df, [‘col_3’] = df.aplicar(get_sublist, eje=1) da ‘ValueError: operandos no podía ser de difusión junto con figuras (2) (3)’
    • con Pandas versión 0.14.1 (y posiblemente antes), podemos usar una expresión lambda así. Dar la df objeto definido, otro enfoque (con resultados equivalentes) es df.apply(lambda x: x[0] + x[1], axis = 1).
    • Es posible tener f() devolver un dict? Cuando lo intento, me da <built-in method values of dict object at 0x10... he mirado en este, y el comentario de «una rama se toma suponiendo que los pandas pueden llamar someobj.values» parece sugerir que no se puede hacer?
    • Sí, buen punto. De hecho, el OP de lambdas (de nuevo en 2012!). Yo era sólo igualar el OP del formato en mi respuesta.
    • Debe estar bien devolver un dict. Tal vez empezar una nueva pregunta si usted está teniendo problemas.
    • esto ya no funciona: TypeError: ('f() takes exactly 2 arguments (1 given)', u'occurred at index 0')
    • gracias por tu respuesta. Es allí una manera de pasar un argumento así? Mi objetivo es en realidad para pasar los índices de las columnas como parámetro, así que cuando el orden de las columnas cambios, podemos enviar fácilmente los diferentes índices.
    • usted sólo puede usar los nombres de columna en la función en lugar de los índices, a continuación, usted no necesita preocuparse acerca de la orden de cambio, o de obtener el índice por nombre por ejemplo, véase stackoverflow.com/questions/13021654/…

  2. 68

    Una solución es simple:

    df['col_3'] = df[['col_1','col_2']].apply(lambda x: f(*x), axis=1)
    • cómo es esta respuesta diferente a la del enfoque en thequestion: df[‘col_3’] = df[[‘col_1′,’col_2’]].aplicar(f) sólo para confirmar, el enfoque en la pregunta no funcionó porque el cartel no se especifica este eje=1, el valor predeterminado es eje = 0?
    • Esta respuesta es comparable a @Anman la respuesta, pero un poco más pulido. Él es la construcción de una función anónima que lleva un iterable, y desmontado antes de pasar a la función f.
  3. 66

    Limpia, una línea de la forma de hacer esto en los Pandas:

    df['col_3'] = df.apply(lambda x: f(x.col_1, x.col_2), axis=1)

    Esto permite f a ser una función definida por el usuario con varios valores de entrada, y los usos (safe) de la columna de los nombres de lugar (inseguro) los índices numéricos para acceder a las columnas.

    Ejemplo con datos (basado en el original de la pregunta):

    import pandas as pd
    
    df = pd.DataFrame({'ID':['1', '2', '3'], 'col_1': [0, 2, 3], 'col_2':[1, 4, 5]})
    mylist = ['a', 'b', 'c', 'd', 'e', 'f']
    
    def get_sublist(sta,end):
        return mylist[sta:end+1]
    
    df['col_3'] = df.apply(lambda x: get_sublist(x.col_1, x.col_2), axis=1)

    Salida de print(df):

      ID  col_1  col_2      col_3
    0  1      0      1     [a, b]
    1  2      2      4  [c, d, e]
    2  3      3      5  [d, e, f]
    • Nota, si el uso de axis=1 y que la columna se llama name no se devuelven los datos de columna, pero la index. Similares como conseguir el name en un groupby(). Lo resuelto por el cambio de nombre de mi columna.
    • ESTO ES! Yo simplemente no se dan cuenta de que usted podría insertar funciones definidas por el usuario con múltiples parámetros de entrada en funciones lambda. Es importante tener en cuenta (creo) que estás utilizando DF.aplicar() en lugar de en Serie.aplicar(). Esto le permite indexar el df el uso de las dos columnas que desea, y pasar toda la columna en la función, sino porque estás usando aplicar(), se aplica la función de un elemento sabio de la moda a lo largo de la columna. Genial! Gracias por publicar!
    • Por fin! Usted salvó mi día!
  4. 38

    Una pregunta interesante! mi respuesta como la siguiente:

    import pandas as pd
    
    def sublst(row):
        return lst[row['J1']:row['J2']]
    
    df = pd.DataFrame({'ID':['1','2','3'], 'J1': [0,2,3], 'J2':[1,4,5]})
    print df
    lst = ['a','b','c','d','e','f']
    
    df['J3'] = df.apply(sublst,axis=1)
    print df

    De salida:

      ID  J1  J2
    0  1   0   1
    1  2   2   4
    2  3   3   5
      ID  J1  J2      J3
    0  1   0   1     [a]
    1  2   2   4  [c, d]
    2  3   3   5  [d, e]

    He cambiado el nombre de columna ID,J1,J2,J3 para garantizar ID < J1 < J2 < J3, de modo que la columna de la pantalla en la secuencia correcta.

    Uno más breve versión:

    import pandas as pd
    
    df = pd.DataFrame({'ID':['1','2','3'], 'J1': [0,2,3], 'J2':[1,4,5]})
    print df
    lst = ['a','b','c','d','e','f']
    
    df['J3'] = df.apply(lambda row:lst[row['J1']:row['J2']],axis=1)
    print df
  5. 19

    El método que se busca es la Serie.combinar.
    Sin embargo, parece que algunos de cuidado tiene que ser tomado en torno a los tipos de datos.
    En tu ejemplo, usted (como yo lo hice cuando las pruebas de la respuesta), ingenuamente, llamada

    df['col_3'] = df.col_1.combine(df.col_2, func=get_sublist)

    Sin embargo, este lanza un error:

    ValueError: setting an array element with a sequence.

    Mi mejor conjetura es que parece esperar que el resultado sea del mismo tipo que el de la serie llamada al método (df.col_1 aquí). Sin embargo, las siguientes obras:

    df['col_3'] = df.col_1.astype(object).combine(df.col_2, func=get_sublist)
    
    df
    
       ID   col_1   col_2   col_3
    0   1   0   1   [a, b]
    1   2   2   4   [c, d, e]
    2   3   3   5   [d, e, f]
    • Con mi peculiar función, esta realmente funcionó!!
  6. 12

    La forma en que usted ha escrito f se necesita de dos entradas. Si usted mira el mensaje de error que dice no se proporciona dos entradas para f, sólo uno. El mensaje de error es correcta.

    El desajuste es porque df[[‘col1′,’col2’]] devuelve un único dataframe con dos columnas, no dos columnas separadas.

    Usted necesita cambiar su f de modo que se necesita una única entrada, mantenga el anterior marco de datos como entrada, entonces se rompen en x,y dentro de el cuerpo de la función. A continuación, hacer lo que sea necesario y devolver un único valor.

    Necesita esta función la firma porque la sintaxis es .aplicar(f)
    Así que f necesita tomar la única cosa = dataframe y no dos cosas, que es lo que su actual f espera.

    Dado que no siempre el cuerpo de f no te puedo ayudar en más detalle -, pero esto debería proporcionar la manera de salir sin cambiar fundamentalmente el código o el uso de otros métodos en lugar de aplicar

  7. 12

    Me voy a poner en un voto de la np.vectorización. Esto permite que usted acaba de disparar más de x número de columnas y no tratar con el dataframe en la función, así que es ideal para funciones que no de control o haciendo algo así como el envío de 2 columnas y una constante en una función (es decir, col_1, col_2, ‘foo’).

    import numpy as np
    import pandas as pd
    
    df = pd.DataFrame({'ID':['1','2','3'], 'col_1': [0,2,3], 'col_2':[1,4,5]})
    mylist = ['a','b','c','d','e','f']
    
    def get_sublist(sta,end):
        return mylist[sta:end+1]
    
    #df['col_3'] = df[['col_1','col_2']].apply(get_sublist,axis=1)
    # expect above to output df as below 
    
    df.loc[:,'col_3'] = np.vectorize(get_sublist, otypes=["O"]) (df['col_1'], df['col_2'])
    
    
    df
    
    ID  col_1   col_2   col_3
    0   1   0   1   [a, b]
    1   2   2   4   [c, d, e]
    2   3   3   5   [d, e, f]
    • Esto en realidad no responder a la pregunta utilizando los pandas.
    • La pregunta es «Cómo aplicar una función a dos columnas de Pandas dataframe» no «Cómo aplicar una función a dos columnas de Pandas dataframe utilizando sólo los Pandas métodos» y numpy es una dependencia de Pandas así que tienes que tener instalado de todos modos, así que esto parece un extraño objeción.
  8. 6

    Devolver una lista de apply es una operación peligrosa como el objeto resultante no está garantizada para ser una Serie o un DataFrame. Y las excepciones podrían ser planteadas en ciertos casos. Vamos a caminar a través de un sencillo ejemplo:

    df = pd.DataFrame(data=np.random.randint(0, 5, (5,3)),
                      columns=['a', 'b', 'c'])
    df
       a  b  c
    0  4  0  0
    1  2  0  1
    2  2  2  2
    3  1  2  2
    4  3  0  0

    Hay tres resultados posibles con devolver una lista de apply

    1) Si la longitud de la lista devuelta no es igual al número de columnas, a continuación, una Serie de listas que se devuelve.

    df.apply(lambda x: list(range(2)), axis=1)  # returns a Series
    0    [0, 1]
    1    [0, 1]
    2    [0, 1]
    3    [0, 1]
    4    [0, 1]
    dtype: object

    2) Cuando la longitud de la lista devuelta es igual al número de
    columnas, a continuación, un DataFrame se devuelve y cada columna se presenta el
    valor correspondiente en la lista.

    df.apply(lambda x: list(range(3)), axis=1) # returns a DataFrame
       a  b  c
    0  0  1  2
    1  0  1  2
    2  0  1  2
    3  0  1  2
    4  0  1  2

    3) Si la longitud de la lista devuelta es igual al número de columnas de la primera fila, pero tiene al menos una fila en la lista tiene un número diferente de elementos que el número de columnas de una ValueError es elevada.

    i = 0
    def f(x):
        global i
        if i == 0:
            i += 1
            return list(range(3))
        return list(range(4))
    
    df.apply(f, axis=1) 
    ValueError: Shape of passed values is (5, 4), indices imply (5, 3)

    Responder el problema sin aplicar

    Utilizando apply con eje=1 es muy lento. Es posible obtener un rendimiento mucho mejor (especialmente en grandes conjuntos de datos) básicos de los métodos iterativos.

    Crear grandes dataframe

    df1 = df.sample(100000, replace=True).reset_index(drop=True)

    Tiempos

    # apply is slow with axis=1
    %timeit df1.apply(lambda x: mylist[x['col_1']: x['col_2']+1], axis=1)
    2.59 s ± 76.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
    
    # zip - similar to @Thomas
    %timeit [mylist[v1:v2+1] for v1, v2 in zip(df1.col_1, df1.col_2)]  
    29.5 ms ± 534 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

    @Thomas respuesta

    %timeit list(map(get_sublist, df1['col_1'],df1['col_2']))
    34 ms ± 459 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
  9. 5

    Estoy seguro de que esto no es tan rápido como las soluciones mediante el uso de los Pandas o Numpy operaciones, pero si usted no quiere reescribir su función puede utilizar el mapa. Utilizando el ejemplo original de datos

    import pandas as pd
    
    df = pd.DataFrame({'ID':['1','2','3'], 'col_1': [0,2,3], 'col_2':[1,4,5]})
    mylist = ['a','b','c','d','e','f']
    
    def get_sublist(sta,end):
        return mylist[sta:end+1]
    
    df['col_3'] = list(map(get_sublist,df['col_1'],df['col_2']))
    #In Python 2 don't convert above to list

    Nos podía pasar tantos argumentos como queríamos en la función de esta forma. El resultado es lo que quería

    ID  col_1  col_2      col_3
    0  1      0      1     [a, b]
    1  2      2      4  [c, d, e]
    2  3      3      5  [d, e, f]
    • Esto es en realidad mucho más rápido esas respuestas que utilizar apply con axis=1
  10. 2

    Mi ejemplo a sus preguntas:

    def get_sublist(row, col1, col2):
        return mylist[row[col1]:row[col2]+1]
    df.apply(get_sublist, axis=1, col1='col_1', col2='col_2')
  11. 1

    Supongo que no quieres cambiar get_sublist función, y sólo desea utilizar DataFrame del apply método para hacer el trabajo. Para obtener el resultado que quiero, yo he escrito dos funciones de ayuda: get_sublist_list y unlist. Como el nombre de la función a sugerir, en primer lugar, obtener la lista de sublista, segundo extracto que sublista de la lista. Por último, Tenemos que llamar a apply función de aplicar las dos funciones a la df[['col_1','col_2']] DataFrame posteriormente.

    import pandas as pd
    
    df = pd.DataFrame({'ID':['1','2','3'], 'col_1': [0,2,3], 'col_2':[1,4,5]})
    mylist = ['a','b','c','d','e','f']
    
    def get_sublist(sta,end):
        return mylist[sta:end+1]
    
    def get_sublist_list(cols):
        return [get_sublist(cols[0],cols[1])]
    
    def unlist(list_of_lists):
        return list_of_lists[0]
    
    df['col_3'] = df[['col_1','col_2']].apply(get_sublist_list,axis=1).apply(unlist)
    
    df

    Si no uso [] para encerrar el get_sublist función, entonces el get_sublist_list función devolverá una simple lista, que va a elevar ValueError: could not broadcast input array from shape (3) into shape (2), como @Ted Petrou había mencionado.

  12. 0

    Si usted tiene un gran conjunto de datos, entonces usted puede utilizar un sencillo pero más rápido(tiempo de ejecución) manera de hacer esto usando más rápida:

    import pandas as pd
    import swifter
    
    def fnc(m,x,c):
        return m*x+c
    
    df = pd.DataFrame({"m": [1,2,3,4,5,6], "c": [1,1,1,1,1,1], "x":[5,3,6,2,6,1]})
    df["y"] = df.swifter.apply(lambda x: fnc(x.m, x.x, x.c), axis=1)

Dejar respuesta

Please enter your comment!
Please enter your name here