python dataframe concatenate two columns
¿Por qué la concatenación de DataFrames se vuelve exponencialmente más lenta? (2)
Tengo una función que procesa un DataFrame, en gran parte para procesar datos en cubos, crear una matriz binaria de características en una columna particular usando
pd.get_dummies(df[col])
.
Para evitar procesar todos mis datos usando esta función de una vez (que se queda sin memoria y hace que iPython se bloquee), he dividido el gran DataFrame en fragmentos usando:
chunks = (len(df) / 10000) + 1
df_list = np.array_split(df, chunks)
pd.get_dummies(df)
creará automáticamente nuevas columnas basadas en el contenido de
df[col]
y es probable que estas difieran para cada
df
en
df_list
.
Después del procesamiento, estoy concatenando los DataFrames nuevamente usando:
for i, df_chunk in enumerate(df_list):
print "chunk", i
[x, y] = preprocess_data(df_chunk)
super_x = pd.concat([super_x, x], axis=0)
super_y = pd.concat([super_y, y], axis=0)
print datetime.datetime.utcnow()
El tiempo de procesamiento del primer fragmento es perfectamente aceptable, sin embargo, ¡crece por fragmento!
Esto no tiene que ver con
preprocess_data(df_chunk)
ya que no hay razón para que aumente.
¿Se produce este aumento de tiempo como resultado de la llamada a
pd.concat()
?
Por favor, consulte el registro a continuación:
chunks 6
chunk 0
2016-04-08 00:22:17.728849
chunk 1
2016-04-08 00:22:42.387693
chunk 2
2016-04-08 00:23:43.124381
chunk 3
2016-04-08 00:25:30.249369
chunk 4
2016-04-08 00:28:11.922305
chunk 5
2016-04-08 00:32:00.357365
¿Hay alguna solución para acelerar esto? Tengo 2900 fragmentos para procesar, por lo que agradeceré cualquier ayuda.
¡Abierto a cualquier otra sugerencia en Python!
Cada vez que concatena, devuelve una copia de los datos.
Desea mantener una lista de sus fragmentos y luego concatenar todo como el paso final.
df_x = []
df_y = []
for i, df_chunk in enumerate(df_list):
print "chunk", i
[x, y] = preprocess_data(df_chunk)
df_x.append(x)
df_y.append(y)
super_x = pd.concat(df_x, axis=0)
del df_x # Free-up memory.
super_y = pd.concat(df_y, axis=0)
del df_y # Free-up memory.
Nunca llame a
DataFrame.append
o
pd.concat
dentro de un ciclo for.
Conduce a la copia cuadrática.
pd.concat
devuelve un nuevo DataFrame.
Se debe asignar espacio para el nuevo DataFrame, y los datos de los antiguos DataFrames se deben copiar en el nuevo DataFrame.
Considere la cantidad de copia requerida por esta línea dentro del
for-loop
(suponiendo que cada
x
tenga tamaño 1):
super_x = pd.concat([super_x, x], axis=0)
| iteration | size of old super_x | size of x | copying required |
| 0 | 0 | 1 | 1 |
| 1 | 1 | 1 | 2 |
| 2 | 2 | 1 | 3 |
| ... | | | |
| N-1 | N-1 | 1 | N |
1 + 2 + 3 + ... + N = N(N+1)/2
.
Por lo tanto, se requieren
O(N**2)
copias para completar el ciclo.
Ahora considera
super_x = []
for i, df_chunk in enumerate(df_list):
[x, y] = preprocess_data(df_chunk)
super_x.append(x)
super_x = pd.concat(super_x, axis=0)
Agregar a una lista es una operación
O(1)
y no requiere copia.
Ahora hay una sola llamada a
pd.concat
después de finalizar el ciclo.
Esta llamada a
pd.concat
requiere que se
pd.concat
N copias, ya que
super_x
contiene
N
DataFrames de tamaño 1. Entonces, cuando se construye de esta manera,
super_x
requiere
O(N)
copias.