preloader

Combinación de estructuras de datos

Índice de contenido

Pandas cuenta con múltiples funcionalidades para combinar de forma sencilla varias Series o DataFrames.

python
import pandas as pd

pd.concat(): concatenación de objetos

pd.concat() permite concatenar objetos a lo largo del eje X (“pegando” unas filas debajo de otras) o a lo largo del eje Y (“pegando” unas columnas al lado de otras). Por supuesto, en el caso de las Series solo hay un posible eje a lo largo del cual concatenar.

Esta función toma una lista o un diccionario de objetos del mismo tipo y los concatena, permitiendo especificar algunas opciones acerce de “qué hacer con los otros ejes”.

Vamos a ver los parámetros más relevantes:

  • axis indica el eje a lo largo del cual se quieren concatenar los objetos. Por defecto es 0 (para los DataFrames, este es el eje de las filas, mientras que para las Series este es el único eje posible), aunque puede ser 0,1…
  • join permite indicar “qué hacer con los otros ejes”:
    • Si se quiere llevar a cabo una unión, hay que fijar el parámetro join=“outer”. Esta es la opción por defecto, ya que no conlleva ninguna pérdida de información.
    • Para llevar a cabo una intersección, hay que fijar join=“inner”. Esta opción, en muchos casos, provocará una pérdida de información.
  • ignore_index permite indicar si se desea ignorar los índices originales de las estructuras de datos que se están combinando. Si es “True”, entonces creará un nuevo índice automático de números consecutivos, empezando en 0.
python
uno = pd.DataFrame({"col1": [1, 2, 3],
                   "col2": [4, 5, 6],
                   "col3": [7, 8, 9]},
                  index = ["id1", "id2", "id3"])
uno

col1col2col3
id1147
id2258
id3369
python
dos = pd.DataFrame({"col1": [10, 11, 12, 13],
                   "col2": [14, 15, 16, 17],
                   "col3": [18, 19, 20, 21]})
dos

col1col2col3
0101418
1111519
2121620
3131721
python
tres = pd.DataFrame({"col1": ["a", "b", "c"],
                   "col2": ["d", "e", "f"],
                   "col3": ["g", "h", "i"],
                   "col4": ["j", "k", "l"]},
                  index = ["n1", "n2", "n3"])
tres

col1col2col3col4
n1adgj
n2behk
n3cfil
python
cuatro = pd.DataFrame({"col1": ["a", "b", "c"],
                   "col2": ["d", "e", "f"],
                   "col3": ["g", "h", "i"],
                   "col4": ["j", "k", "l"]},
                  index = ["id1", "id2", "id3"])
cuatro

col1col2col3col4
id1adgj
id2behk
id3cfil
python
uno

col1col2col3
id1147
id2258
id3369
python
dos

col1col2col3
0101418
1111519
2121620
3131721
python
tres

col1col2col3col4
n1adgj
n2behk
n3cfil
python
cuatro

col1col2col3col4
id1adgj
id2behk
id3cfil
python
pd.concat([uno, dos],
         axis = 0,
         join = "outer",
         ignore_index = False)

col1col2col3
id1147
id2258
id3369
0101418
1111519
2121620
3131721
python
pd.concat([uno, dos],
         axis = 0,
         join = "inner",
         ignore_index = True)

col1col2col3
0147
1258
2369
3101418
4111519
5121620
6131721
python
pd.concat([uno, dos],
         axis = 1,
         join = "outer",
         ignore_index = False)

col1col2col3col1col2col3
id11.04.07.0NaNNaNNaN
id22.05.08.0NaNNaNNaN
id33.06.09.0NaNNaNNaN
0NaNNaNNaN10.014.018.0
1NaNNaNNaN11.015.019.0
2NaNNaNNaN12.016.020.0
3NaNNaNNaN13.017.021.0
python
pd.concat([uno, dos],
         axis = 1,
         join = "inner",
         ignore_index = False)

col1col2col3col1col2col3
python
pd.concat([uno, dos],
         axis = 1,
         join = "inner",
         ignore_index = True)

012345
python
pd.concat([uno, cuatro],
         axis = 0,
         join = "outer",
         ignore_index = False)

col1col2col3col4
id1147NaN
id2258NaN
id3369NaN
id1adgj
id2behk
id3cfil
python
pd.concat([uno, cuatro],
         axis = 0,
         join = "inner",
         ignore_index = False)

col1col2col3
id1147
id2258
id3369
id1adg
id2beh
id3cfi

También es posible concatenar una combinación de objetos de tipo Serie y DataFrame. La Serie se transformará en DataFrame con el nombre de la columna como el nombre de la serie. Si se pasan Series sin nombres concretos, se numerarán consecutivamente.

python
serie1 = pd.Series(["s1", "s2", "s3"],
                  name = "col5",
                  index = ["n1", "n2", "n3"])
serie1
n1    s1
n2    s2
n3    s3
Name: col5, dtype: object
python
serie2 = pd.Series(["s1", "s2", "s3"],
                  name = "col5",
                  index = ["id1", "id2", "id3"])
serie2
id1    s1
id2    s2
id3    s3
Name: col5, dtype: object
python
pd.concat([uno, serie1],
         axis = 1,
         join = "outer",
         ignore_index = False)

col1col2col3col5
id11.04.07.0NaN
id22.05.08.0NaN
id33.06.09.0NaN
n1NaNNaNNaNs1
n2NaNNaNNaNs2
n3NaNNaNNaNs3
python
pd.concat([uno, serie2],
         axis = 1,
         join = "outer",
         ignore_index = False)

col1col2col3col5
id1147s1
id2258s2
id3369s3

df.append(): una especie de atajo a pd.concat()

Este método concatena estructuras de datos a lo largo del eje=0, es decir, concatena filas. Las columnas de la segunda estructura que no están en la primera, se agregan como nuevas columnas. Es muy sencillo y tiene muy pocos parámetros, entre los cuales destaca principalmente el siguiente:

  • ignore_index permite indicar si se desea ignorar los índices originales de las estructuras de datos que se están combinando. Si es “True”, entonces creará un nuevo índice automático de números consecutivos, empezando en 0.

En el caso de combinar DataFrames, los índices deben ser diferentes, pero las columnas no necesariamente tienen que serlo.

python
uno.append(dos, ignore_index=False)

col1col2col3
id1147
id2258
id3369
0101418
1111519
2121620
3131721
python
uno.append(dos, ignore_index=True)

col1col2col3
0147
1258
2369
3101418
4111519
5121620
6131721
python
uno.append(tres, ignore_index=False)

col1col2col3col4
id1147NaN
id2258NaN
id3369NaN
n1adgj
n2behk
n3cfil
python
uno.append(cuatro, ignore_index=False, verify_integrity=True)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-9-2d165ced59d6> in <module>
----> 1 uno.append(cuatro, ignore_index=False, verify_integrity=True)

~\anaconda3\envs\python-385\lib\site-packages\pandas\core\frame.py in append(self, other, ignore_index, verify_integrity, sort)
   7740         else:
   7741             to_concat = [self, other]
-> 7742         return concat(
   7743             to_concat,
   7744             ignore_index=ignore_index,

~\anaconda3\envs\python-385\lib\site-packages\pandas\core\reshape\concat.py in concat(objs, axis, join, ignore_index, keys, levels, names, verify_integrity, sort, copy)
    272     ValueError: Indexes have overlapping values: ['a']
    273     """
--> 274     op = _Concatenator(
    275         objs,
    276         axis=axis,

~\anaconda3\envs\python-385\lib\site-packages\pandas\core\reshape\concat.py in __init__(self, objs, axis, join, keys, levels, names, ignore_index, verify_integrity, copy, sort)
    452         self.copy = copy
    453 
--> 454         self.new_axes = self._get_new_axes()
    455 
    456     def get_result(self):

~\anaconda3\envs\python-385\lib\site-packages\pandas\core\reshape\concat.py in _get_new_axes(self)
    517     def _get_new_axes(self) -> List[Index]:
    518         ndim = self._get_result_dim()
--> 519         return [
    520             self._get_concat_axis() if i == self.bm_axis else self._get_comb_axis(i)
    521             for i in range(ndim)

~\anaconda3\envs\python-385\lib\site-packages\pandas\core\reshape\concat.py in <listcomp>(.0)
    518         ndim = self._get_result_dim()
    519         return [
--> 520             self._get_concat_axis() if i == self.bm_axis else self._get_comb_axis(i)
    521             for i in range(ndim)
    522         ]

~\anaconda3\envs\python-385\lib\site-packages\pandas\core\reshape\concat.py in _get_concat_axis(self)
    578             )
    579 
--> 580         self._maybe_check_integrity(concat_axis)
    581 
    582         return concat_axis

~\anaconda3\envs\python-385\lib\site-packages\pandas\core\reshape\concat.py in _maybe_check_integrity(self, concat_index)
    586             if not concat_index.is_unique:
    587                 overlap = concat_index[concat_index.duplicated()].unique()
--> 588                 raise ValueError(f"Indexes have overlapping values: {overlap}")
    589 
    590 

ValueError: Indexes have overlapping values: Index(['id1', 'id2', 'id3'], dtype='object')

Es importante tener en cuenta que concat() y append() hacen una copia completa de los datos y que, por tanto, su reutilización constante puede causar un impacto negativo en el rendimiento.

df.merge()

Pandas también cuenta con un join muy similar al que se utiliza en SQL, pero en este caso se llama merge(). Veamos los parámetros más importantes:

  • left es un DataFrame o una serie.

  • right es otro DataFrame o serie.

  • on son los nombres de columna o niveles de índice para unirse, que deben estar presentes tanto en el DataFrame de la izquierda como en el de la derecha.

  • left_on son las columnas o niveles de índice del DataFrame o Serie de la izquierda para usar como claves.

  • right_on son las columnas o niveles de índice del DataFrame o Serie de la derecha para usar como claves.

  • how puede ser ’left’, ‘right’, ‘outer’ o ‘inner’. Por defecto es ‘inner’.

python
uno.merge(dos, 
         how='inner',
         on = "col1")

col1col2_xcol3_xcol2_ycol3_y
python
cinco = pd.DataFrame({"col1": ["b", "c", "a"],
                     "col2": [10, 20, 30]})
cinco

col1col2
0b10
1c20
2a30
python
tres.merge(cinco, 
         how='inner',
         on = "col1")

col1col2_xcol3col4col2_y
0adgj30
1behk10
2cfil20
python
uno.merge(dos, 
         how='outer',
         on = "col1")

col1col2_xcol3_xcol2_ycol3_y
014.07.0NaNNaN
125.08.0NaNNaN
236.09.0NaNNaN
310NaNNaN14.018.0
411NaNNaN15.019.0
512NaNNaN16.020.0
613NaNNaN17.021.0
python
uno.merge(dos, 
         how='outer',
         on = "col1",
         suffixes=('_dfizq', '_dfder'))

col1col2_dfizqcol3_dfizqcol2_dfdercol3_dfder
014.07.0NaNNaN
125.08.0NaNNaN
236.09.0NaNNaN
310NaNNaN14.018.0
411NaNNaN15.019.0
512NaNNaN16.020.0
613NaNNaN17.021.0
python
dos.merge(uno, 
         on = "col1",
         how='left')

col1col2_xcol3_xcol2_ycol3_y
0101418NaNNaN
1111519NaNNaN
2121620NaNNaN
3131721NaNNaN
python
dos.merge(uno, 
         on = "col1",
         how='right')

col1col2_xcol3_xcol2_ycol3_y
01NaNNaN47
12NaNNaN58
23NaNNaN69
python
seis = pd.DataFrame({"c_1": [1,2],
                    "c_2": ["a", "b"]})
seis

c_1c_2
01a
12b
python
uno.merge(seis, 
         how="inner",
         left_on = "col1",
         right_on = "c_1")

col1col2col3c_1c_2
01471a
12582b
python
uno.merge(seis, 
         how="left",
         left_on = "col1",
         right_on = "c_1")

col1col2col3c_1c_2
01471.0a
12582.0b
2369NaNNaN
python
uno.merge(seis, 
         how="right",
         left_on = "col1",
         right_on = "c_1")

col1col2col3c_1c_2
01471a
12582b
python
uno.merge(seis, 
         how="outer",
         left_on = "col1",
         right_on = "c_1")

col1col2col3c_1c_2
01471.0a
12582.0b
2369NaNNaN

df.join()

df.join() permite unir columnas de un DataFrame a las de otro. Sus principales parámetros son:

  • on son los nombres de las columnas o nivel de índice de un DataFrame sobre los cuales se unirá el otro.

  • how puede ser ‘left’ (por defecto), ‘right’, ‘outer’ o ‘inner’.

Las siguientes operaciones son completamente equivalentes:

left.join(right, on=key_or_keys)

left.merge(right, left_on=key_or_keys, right_index=True, how=‘left’, sort=False)

python
eventos1 = pd.DataFrame({"Evento": ["Id1", "Id2", "Id3"],
                        "Encargado": ["Roberto C.", "Jorge L.", "Lucía M."]},
                        index = ['2018-01-01 00:00:00', 
                                 '2018-01-01 01:00:00',
                                 '2018-01-01 02:00:00'])
python
eventos2 = pd.DataFrame({"Ubicación": ["ss_2", "ss_11", "ss_2", "ss_6", "ss_4"],
                        "Repetido": [True, False, False, True, True]},
                        index = ['2018-01-01 00:00:00', 
                                 '2018-01-01 01:00:00',
                                 '2018-01-01 02:00:00',
                                 '2018-01-01 03:00:00',
                                 '2018-01-01 04:00:00'])
python
eventos1

EventoEncargado
2018-01-01 00:00:00Id1Roberto C.
2018-01-01 01:00:00Id2Jorge L.
2018-01-01 02:00:00Id3Lucía M.
python
eventos2

UbicaciónRepetido
2018-01-01 00:00:00ss_2True
2018-01-01 01:00:00ss_11False
2018-01-01 02:00:00ss_2False
2018-01-01 03:00:00ss_6True
2018-01-01 04:00:00ss_4True
python
eventos1.join(eventos2,
            how = "left")

EventoEncargadoUbicaciónRepetido
2018-01-01 00:00:00Id1Roberto C.ss_2True
2018-01-01 01:00:00Id2Jorge L.ss_11False
2018-01-01 02:00:00Id3Lucía M.ss_2False
python
eventos1.join(eventos2,
            how = "right")

EventoEncargadoUbicaciónRepetido
2018-01-01 00:00:00Id1Roberto C.ss_2True
2018-01-01 01:00:00Id2Jorge L.ss_11False
2018-01-01 02:00:00Id3Lucía M.ss_2False
2018-01-01 03:00:00NaNNaNss_6True
2018-01-01 04:00:00NaNNaNss_4True
python
eventos1.join(eventos2,
            how = "inner")

EventoEncargadoUbicaciónRepetido
2018-01-01 00:00:00Id1Roberto C.ss_2True
2018-01-01 01:00:00Id2Jorge L.ss_11False
2018-01-01 02:00:00Id3Lucía M.ss_2False
python
eventos1.join(eventos2,
            how = "outer")

EventoEncargadoUbicaciónRepetido
2018-01-01 00:00:00Id1Roberto C.ss_2True
2018-01-01 01:00:00Id2Jorge L.ss_11False
2018-01-01 02:00:00Id3Lucía M.ss_2False
2018-01-01 03:00:00NaNNaNss_6True
2018-01-01 04:00:00NaNNaNss_4True
comments powered by Disqus