python - recorrer - seleccionar columnas de un dataframe pandas
python pandas dataframe, ¿es paso por valor o paso por referencia (6)
Aquí está el documento para soltar:
Devuelve un nuevo objeto con etiquetas en el eje solicitado eliminado.
Entonces se crea un nuevo marco de datos. El original no ha cambiado.
Pero como para todos los objetos en python, el marco de datos se pasa a la función por referencia.
Si paso un marco de datos a una función y lo modifico dentro de la función, ¿es paso por valor o paso por referencia?
Ejecuto el siguiente código
a = pd.DataFrame({''a'':[1,2], ''b'':[3,4]})
def letgo(df):
df = df.drop(''b'',axis=1)
letgo(a)
el valor de
a
no cambia después de la llamada a la función.
¿Significa que es paso por valor?
También probé lo siguiente
xx = np.array([[1,2], [3,4]])
def letgo2(x):
x[1,1] = 100
def letgo3(x):
x = np.array([[3,3],[3,3]])
Resulta que
letgo2()
cambia
xx
y
letgo3()
no.
¿Por qué es como este?
La pregunta no es PBV vs. PBR. Estos nombres solo causan confusión en un lenguaje como Python; fueron inventados para lenguajes que funcionan como C o Fortran (como los lenguajes PBV y PBR por excelencia). Es cierto, pero no esclarecedor, que Python siempre pasa por valor. La pregunta aquí es si el valor en sí está mutado o si obtiene un nuevo valor. Los pandas generalmente se equivocan del lado de este último.
http://nedbatchelder.com/text/names.html explica muy bien cuál es el sistema de nombres de Python.
La respuesta corta es que Python siempre pasa por valor, pero cada variable de Python es en realidad un puntero a algún objeto, por lo que a veces parece pasar por referencia.
En Python cada objeto es mutable o no mutable. por ejemplo, las listas, los dictos, los módulos y los marcos de datos de Pandas son mutables, y las entradas, cadenas y tuplas no son mutables. Los objetos mutables se pueden cambiar internamente (por ejemplo, agregar un elemento a una lista), pero los objetos no mutables no.
Como dije al principio, puedes pensar en cada variable de Python como un puntero a un objeto. Cuando pasa una variable a una función, la variable (puntero) dentro de la función es siempre una copia de la variable (puntero) que se pasó. Entonces, si asigna algo nuevo a la variable interna, todo lo que está haciendo es cambiar la variable local para apuntar a un objeto diferente. Esto no altera (muta) el objeto original al que apuntaba la variable, ni hace que la variable externa apunte al nuevo objeto. En este punto, la variable externa todavía apunta al objeto original, pero la variable interna apunta a un nuevo objeto.
Si desea modificar el objeto original (solo es posible con tipos de datos mutables), debe hacer algo que altere el objeto
sin
asignar un valor completamente nuevo a la variable local.
Es por eso que
letgo()
y
letgo3()
dejan el elemento externo inalterado, pero
letgo2()
altera.
Como señaló @ursan, si
letgo()
usara algo como esto en su lugar, alteraría (mutaría) el objeto original al que apunta
df
, lo que cambiaría el valor visto a través de la variable global
a
:
def letgo(df):
df.drop(''b'', axis=1, inplace=True)
a = pd.DataFrame({''a'':[1,2], ''b'':[3,4]})
letgo(a) # will alter a
En algunos casos, puede vaciar completamente la variable original y rellenarla con nuevos datos, sin hacer una asignación directa, por ejemplo, esto alterará el objeto original al que
v
apunta, lo que cambiará los datos que se ven cuando usa
v
más tarde:
def letgo3(x):
x[:] = np.array([[3,3],[3,3]])
v = np.empty((2, 2))
letgo3(v) # will alter v
Tenga en cuenta que no estoy asignando algo directamente a
x
;
Estoy asignando algo a todo el rango interno de
x
.
Si absolutamente debe crear un objeto completamente nuevo y hacerlo visible externamente (que a veces es el caso con los pandas), tiene dos opciones. La opción ''limpiar'' sería simplemente devolver el nuevo objeto, por ejemplo,
def letgo(df):
df = df.drop(''b'',axis=1)
return df
a = pd.DataFrame({''a'':[1,2], ''b'':[3,4]})
a = letgo(a)
Otra opción sería llegar fuera de su función y alterar directamente una variable global.
Esto cambia
a
para apuntar a un nuevo objeto, y cualquier función que se refiera a
a
posterior verá ese nuevo objeto:
def letgo():
global a
a = a.drop(''b'',axis=1)
a = pd.DataFrame({''a'':[1,2], ''b'':[3,4]})
letgo() # will alter a!
La modificación directa de las variables globales suele ser una mala idea, porque cualquiera que lea su código tendrá dificultades para descubrir cómo se modificó. (Generalmente uso variables globales para parámetros compartidos usados por muchas funciones en un script, pero no dejo que alteren esas variables globales).
Para agregar a la respuesta de @Mike Graham, quien señaló una muy buena lectura:
En su caso, lo que es importante recordar es la diferencia entre
nombres
y
valores
.
a
,
df
,
xx
,
x
, son todos
nombres
, pero se refieren a los mismos o diferentes
valores
en diferentes puntos de sus ejemplos:
-
En el primer ejemplo,
letgo
vuelve a vinculardf
a otro valor, porquedf.drop
devuelve un nuevoDataFrame
menos que establezca el argumentoinplace = True
( consulte el documento ). Eso significa que el nombredf
(local para la funciónletgo
), que se refería al valor dea
, ahora se refiere a un nuevo valor, aquí el valor de retornodf.drop
. El valora
se refiere a todavía existe y no ha cambiado. -
En el segundo ejemplo,
letgo2
mutax
, sinletgo2
, razón por la cualxx
es modificado porletgo2
. A diferencia del ejemplo anterior, aquí el nombre localx
siempre se refiere al valor al que se refiere el nombrexx
, y cambia ese valor en su lugar , razón por la cual el valor al que se refierexx
ha cambiado. -
En el tercer ejemplo,
letgo3
vuelve a vincularx
a un nuevonp.array
. Eso hace que el nombrex
, local, sealetgo3
y que anteriormente se refiriera al valor dexx
, ahora se refiera a otro valor, el nuevonp.array
. El valor al que se refierexx
no ha cambiado.
Python no es pasar por valor ni pasar por referencia. Es pasar por encargo.
Referencia de soporte, las preguntas frecuentes de Python: https://docs.python.org/3/faq/programming.html#how-do-i-write-a-function-with-output-parameters-call-by-reference
IOW:
- Si pasa un valor inmutable, los cambios en él no cambian su valor en la persona que llama, porque está volviendo a vincular el nombre a un nuevo objeto.
- Si pasa un valor mutable, los cambios realizados en la función llamada, también cambian el valor en la persona que llama, siempre que no vuelva a vincular ese nombre a un nuevo objeto. Si reasigna la variable, creando un nuevo objeto, ese cambio y los cambios posteriores al nombre no se ven en la persona que llama.
Entonces, si pasa una lista y cambia su valor 0, ese cambio se ve tanto en el llamado como en el que llama. Pero si reasigna la lista con una nueva lista, este cambio se pierde. Pero si corta la lista y la reemplaza con una nueva lista, ese cambio se ve tanto en el llamado como en la persona que llama.
P.EJ:
def change_it(list_):
# This change would be seen in the caller if we left it alone
list_[0] = 28
# This change is also seen in the caller, and replaces the above
# change
list_[:] = [1, 2]
# This change is not seen in the caller.
# If this were pass by reference, this change too would be seen in
# caller.
list_ = [3, 4]
thing = [10, 20]
change_it(thing)
# here, thing is [1, 2]
Si eres un fanático de C, puedes pensar en esto como pasar un puntero por valor, no un puntero a un puntero a un valor, solo un puntero a un valor.
HTH.
necesita hacer ''a'' global al comienzo de la función; de lo contrario, es una variable local y no cambia la ''a'' en el código principal.