Anexando tablas#

Al procesar múltiples imágenes, potencialmente utilizando varias bibliotecas de procesamiento de imágenes, una tarea común es combinar tablas.

Comenzamos con dos pequeñas tablas de mediciones que podrían haberse obtenido de diferentes funciones o diferentes bibliotecas.

import pandas as pd
table1 = pd.DataFrame({
    "label":       [1,   2,   3],
    "circularity": [0.3, 0.5, 0.7],
    "elongation":  [2.3, 3.4, 1.2],
    })
table1
label circularity elongation
0 1 0.3 2.3
1 2 0.5 3.4
2 3 0.7 1.2
table2 = pd.DataFrame({
    "label":    [3,   2,   1,   4],
    "area":     [22,  32,  25,  18],
    "skewness": [0.5, 0.6, 0.3, 0.3],
    })
table2
label area skewness
0 3 22 0.5
1 2 32 0.6
2 1 25 0.3
3 4 18 0.3

Combinando columnas de tablas#

Según la documentación de pandas, hay múltiples formas de combinar tablas. Primero usaremos un ejemplo incorrecto para resaltar los errores comunes al combinar tablas.

En el siguiente ejemplo, las mediciones de las etiquetas 1 y 3 están mezcladas. Además, una de nuestras tablas no contenía mediciones para la etiqueta 4.

wrongly_combined_tables = pd.concat([table1, table2], axis=1)
wrongly_combined_tables
label circularity elongation label area skewness
0 1.0 0.3 2.3 3 22 0.5
1 2.0 0.5 3.4 2 32 0.6
2 3.0 0.7 1.2 1 25 0.3
3 NaN NaN NaN 4 18 0.3

Una mejor manera de combinar tablas es el comando merge. Permite especificar explícitamente on qué columna se deben combinar las tablas. Los científicos de datos hablan del ‘índice’ o ‘identificador’ de las filas en las tablas.

correctly_combined_tables1 = pd.merge(table1, table2, how='inner', on='label')
correctly_combined_tables1
label circularity elongation area skewness
0 1 0.3 2.3 25 0.3
1 2 0.5 3.4 32 0.6
2 3 0.7 1.2 22 0.5

Puede notar que en el ejemplo anterior, la etiqueta 4 está ausente. También podemos obtenerla en nuestra tabla realizando una unión externa.

correctly_combined_tables2 = pd.merge(table1, table2, how='outer', on='label')
correctly_combined_tables2
label circularity elongation area skewness
0 1 0.3 2.3 25 0.3
1 2 0.5 3.4 32 0.6
2 3 0.7 1.2 22 0.5
3 4 NaN NaN 18 0.3
correctly_combined_tables2 = pd.merge(table1, table2, how='right', on='label')
correctly_combined_tables2
label circularity elongation area skewness
0 3 0.7 1.2 22 0.5
1 2 0.5 3.4 32 0.6
2 1 0.3 2.3 25 0.3
3 4 NaN NaN 18 0.3

Supongamos que hay un nombre de medición común de diferentes tablas. Por ejemplo, la tabla3 a continuación también contiene “elongation”.

table3 = pd.DataFrame({
    "label":    [3,   2,   1,   4],
    "area":     [22,  32,  25,  18],
    "skewness": [0.5, 0.6, 0.3, 0.3],
    "elongation":  [2.3, 3.4, 1.2, 1.1]
    })
table3
label area skewness elongation
0 3 22 0.5 2.3
1 2 32 0.6 3.4
2 1 25 0.3 1.2
3 4 18 0.3 1.1

Aplicar merge aún preserva ambas mediciones en columnas diferentes.

correctly_combined_tables3 = pd.merge(table1, table3, how='outer', on='label')
correctly_combined_tables3 
label circularity elongation_x area skewness elongation_y
0 1 0.3 2.3 25 0.3 1.2
1 2 0.5 3.4 32 0.6 3.4
2 3 0.7 1.2 22 0.5 2.3
3 4 NaN NaN 18 0.3 1.1

Podemos cambiar ‘x’ e ‘y’ pasando otros sufijos.

correctly_combined_tables3 = pd.merge(table1, table3, how='outer', on='label', suffixes=('_method1', '_method2'))
correctly_combined_tables3
label circularity elongation_method1 area skewness elongation_method2
0 1 0.3 2.3 25 0.3 1.2
1 2 0.5 3.4 32 0.6 3.4
2 3 0.7 1.2 22 0.5 2.3
3 4 NaN NaN 18 0.3 1.1

Combinando mediciones de múltiples archivos de imagen#

Al aplicar un flujo de trabajo a muchas imágenes, obtendrías tablas con los mismos nombres de columnas, pero con un número variable de filas. Para calcular estadísticas para carpetas completas o para realizar aprendizaje automático, generalmente necesitamos concatenar esas tablas, pero es importante mantener un registro de los archivos de origen.

Vamos a abrir dos tablas generadas al aplicar el mismo flujo de trabajo a diferentes archivos.

df1 = pd.read_csv('../../data/BBBC007_20P1_POS0007_D_1UL.csv')
df1.head()
area intensity_mean major_axis_length minor_axis_length aspect_ratio
0 256 93.250000 19.995017 17.021559 1.174688
1 90 82.488889 15.939969 7.516326 2.120713
2 577 90.637782 35.324458 21.759434 1.623409
3 270 95.640741 20.229431 17.669052 1.144908
4 153 84.908497 15.683703 12.420475 1.262730
df2 = pd.read_csv('../../data/BBBC007_20P1_POS0010_D_1UL.csv')
df2.head()
area intensity_mean major_axis_length minor_axis_length aspect_ratio
0 139 96.546763 17.504104 10.292770 1.700621
1 360 86.613889 35.746808 14.983124 2.385805
2 43 91.488372 12.967884 4.351573 2.980045
3 140 73.742857 18.940508 10.314404 1.836316
4 144 89.375000 13.639308 13.458532 1.013432

En este caso particular donde sabemos que tenemos las mismas columnas, podríamos concatenarlas en una sola tabla grande.

big_df = pd.concat([df1, df2], axis=0)
big_df
area intensity_mean major_axis_length minor_axis_length aspect_ratio
0 256 93.250000 19.995017 17.021559 1.174688
1 90 82.488889 15.939969 7.516326 2.120713
2 577 90.637782 35.324458 21.759434 1.623409
3 270 95.640741 20.229431 17.669052 1.144908
4 153 84.908497 15.683703 12.420475 1.262730
... ... ... ... ... ...
42 315 91.133333 20.927095 19.209283 1.089426
43 206 94.262136 23.381879 11.669668 2.003646
44 45 68.377778 9.406371 6.276445 1.498678
45 33 76.727273 10.724275 4.174568 2.568955
46 16 76.750000 7.745967 2.783882 2.782433

111 rows × 5 columns

El problema es que perdemos su identidad de origen. Una solución fácil es agregar una nueva columna con el nombre del archivo antes de concatenarlos. Esto facilitará separarlos nuevamente y graficarlos más adelante.

Cuando asignamos un solo valor a una nueva columna, se asigna a todas las filas.

df1['file_name'] = 'BBBC007_20P1_POS0007_D_1UL'

df2['file_name'] = 'BBBC007_20P1_POS0010_D_1UL'
big_df = pd.concat([df1, df2], axis=0)
big_df
area intensity_mean major_axis_length minor_axis_length aspect_ratio file_name
0 256 93.250000 19.995017 17.021559 1.174688 BBBC007_20P1_POS0007_D_1UL
1 90 82.488889 15.939969 7.516326 2.120713 BBBC007_20P1_POS0007_D_1UL
2 577 90.637782 35.324458 21.759434 1.623409 BBBC007_20P1_POS0007_D_1UL
3 270 95.640741 20.229431 17.669052 1.144908 BBBC007_20P1_POS0007_D_1UL
4 153 84.908497 15.683703 12.420475 1.262730 BBBC007_20P1_POS0007_D_1UL
... ... ... ... ... ... ...
42 315 91.133333 20.927095 19.209283 1.089426 BBBC007_20P1_POS0010_D_1UL
43 206 94.262136 23.381879 11.669668 2.003646 BBBC007_20P1_POS0010_D_1UL
44 45 68.377778 9.406371 6.276445 1.498678 BBBC007_20P1_POS0010_D_1UL
45 33 76.727273 10.724275 4.174568 2.568955 BBBC007_20P1_POS0010_D_1UL
46 16 76.750000 7.745967 2.783882 2.782433 BBBC007_20P1_POS0010_D_1UL

111 rows × 6 columns

Ahora podemos distinguir con seguridad el origen de cada fila.