Pandas cuenta con múltiples funcionalidades para combinar de forma sencilla varias Series o DataFrames.
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
| col1 | col2 | col3 |
|---|
| id1 | 1 | 4 | 7 |
|---|
| id2 | 2 | 5 | 8 |
|---|
| id3 | 3 | 6 | 9 |
|---|
python
dos = pd.DataFrame({"col1": [10, 11, 12, 13],
"col2": [14, 15, 16, 17],
"col3": [18, 19, 20, 21]})
dos
| col1 | col2 | col3 |
|---|
| 0 | 10 | 14 | 18 |
|---|
| 1 | 11 | 15 | 19 |
|---|
| 2 | 12 | 16 | 20 |
|---|
| 3 | 13 | 17 | 21 |
|---|
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
| col1 | col2 | col3 | col4 |
|---|
| n1 | a | d | g | j |
|---|
| n2 | b | e | h | k |
|---|
| n3 | c | f | i | l |
|---|
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
| col1 | col2 | col3 | col4 |
|---|
| id1 | a | d | g | j |
|---|
| id2 | b | e | h | k |
|---|
| id3 | c | f | i | l |
|---|
python
| col1 | col2 | col3 |
|---|
| id1 | 1 | 4 | 7 |
|---|
| id2 | 2 | 5 | 8 |
|---|
| id3 | 3 | 6 | 9 |
|---|
python
| col1 | col2 | col3 |
|---|
| 0 | 10 | 14 | 18 |
|---|
| 1 | 11 | 15 | 19 |
|---|
| 2 | 12 | 16 | 20 |
|---|
| 3 | 13 | 17 | 21 |
|---|
python
| col1 | col2 | col3 | col4 |
|---|
| n1 | a | d | g | j |
|---|
| n2 | b | e | h | k |
|---|
| n3 | c | f | i | l |
|---|
python
| col1 | col2 | col3 | col4 |
|---|
| id1 | a | d | g | j |
|---|
| id2 | b | e | h | k |
|---|
| id3 | c | f | i | l |
|---|
python
pd.concat([uno, dos],
axis = 0,
join = "outer",
ignore_index = False)
| col1 | col2 | col3 |
|---|
| id1 | 1 | 4 | 7 |
|---|
| id2 | 2 | 5 | 8 |
|---|
| id3 | 3 | 6 | 9 |
|---|
| 0 | 10 | 14 | 18 |
|---|
| 1 | 11 | 15 | 19 |
|---|
| 2 | 12 | 16 | 20 |
|---|
| 3 | 13 | 17 | 21 |
|---|
python
pd.concat([uno, dos],
axis = 0,
join = "inner",
ignore_index = True)
| col1 | col2 | col3 |
|---|
| 0 | 1 | 4 | 7 |
|---|
| 1 | 2 | 5 | 8 |
|---|
| 2 | 3 | 6 | 9 |
|---|
| 3 | 10 | 14 | 18 |
|---|
| 4 | 11 | 15 | 19 |
|---|
| 5 | 12 | 16 | 20 |
|---|
| 6 | 13 | 17 | 21 |
|---|
python
pd.concat([uno, dos],
axis = 1,
join = "outer",
ignore_index = False)
| col1 | col2 | col3 | col1 | col2 | col3 |
|---|
| id1 | 1.0 | 4.0 | 7.0 | NaN | NaN | NaN |
|---|
| id2 | 2.0 | 5.0 | 8.0 | NaN | NaN | NaN |
|---|
| id3 | 3.0 | 6.0 | 9.0 | NaN | NaN | NaN |
|---|
| 0 | NaN | NaN | NaN | 10.0 | 14.0 | 18.0 |
|---|
| 1 | NaN | NaN | NaN | 11.0 | 15.0 | 19.0 |
|---|
| 2 | NaN | NaN | NaN | 12.0 | 16.0 | 20.0 |
|---|
| 3 | NaN | NaN | NaN | 13.0 | 17.0 | 21.0 |
|---|
python
pd.concat([uno, dos],
axis = 1,
join = "inner",
ignore_index = False)
python
pd.concat([uno, dos],
axis = 1,
join = "inner",
ignore_index = True)
python
pd.concat([uno, cuatro],
axis = 0,
join = "outer",
ignore_index = False)
| col1 | col2 | col3 | col4 |
|---|
| id1 | 1 | 4 | 7 | NaN |
|---|
| id2 | 2 | 5 | 8 | NaN |
|---|
| id3 | 3 | 6 | 9 | NaN |
|---|
| id1 | a | d | g | j |
|---|
| id2 | b | e | h | k |
|---|
| id3 | c | f | i | l |
|---|
python
pd.concat([uno, cuatro],
axis = 0,
join = "inner",
ignore_index = False)
| col1 | col2 | col3 |
|---|
| id1 | 1 | 4 | 7 |
|---|
| id2 | 2 | 5 | 8 |
|---|
| id3 | 3 | 6 | 9 |
|---|
| id1 | a | d | g |
|---|
| id2 | b | e | h |
|---|
| id3 | c | f | i |
|---|
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)
| col1 | col2 | col3 | col5 |
|---|
| id1 | 1.0 | 4.0 | 7.0 | NaN |
|---|
| id2 | 2.0 | 5.0 | 8.0 | NaN |
|---|
| id3 | 3.0 | 6.0 | 9.0 | NaN |
|---|
| n1 | NaN | NaN | NaN | s1 |
|---|
| n2 | NaN | NaN | NaN | s2 |
|---|
| n3 | NaN | NaN | NaN | s3 |
|---|
python
pd.concat([uno, serie2],
axis = 1,
join = "outer",
ignore_index = False)
| col1 | col2 | col3 | col5 |
|---|
| id1 | 1 | 4 | 7 | s1 |
|---|
| id2 | 2 | 5 | 8 | s2 |
|---|
| id3 | 3 | 6 | 9 | s3 |
|---|
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)
| col1 | col2 | col3 |
|---|
| id1 | 1 | 4 | 7 |
|---|
| id2 | 2 | 5 | 8 |
|---|
| id3 | 3 | 6 | 9 |
|---|
| 0 | 10 | 14 | 18 |
|---|
| 1 | 11 | 15 | 19 |
|---|
| 2 | 12 | 16 | 20 |
|---|
| 3 | 13 | 17 | 21 |
|---|
python
uno.append(dos, ignore_index=True)
| col1 | col2 | col3 |
|---|
| 0 | 1 | 4 | 7 |
|---|
| 1 | 2 | 5 | 8 |
|---|
| 2 | 3 | 6 | 9 |
|---|
| 3 | 10 | 14 | 18 |
|---|
| 4 | 11 | 15 | 19 |
|---|
| 5 | 12 | 16 | 20 |
|---|
| 6 | 13 | 17 | 21 |
|---|
python
uno.append(tres, ignore_index=False)
| col1 | col2 | col3 | col4 |
|---|
| id1 | 1 | 4 | 7 | NaN |
|---|
| id2 | 2 | 5 | 8 | NaN |
|---|
| id3 | 3 | 6 | 9 | NaN |
|---|
| n1 | a | d | g | j |
|---|
| n2 | b | e | h | k |
|---|
| n3 | c | f | i | l |
|---|
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")
| col1 | col2_x | col3_x | col2_y | col3_y |
|---|
python
cinco = pd.DataFrame({"col1": ["b", "c", "a"],
"col2": [10, 20, 30]})
cinco
python
tres.merge(cinco,
how='inner',
on = "col1")
| col1 | col2_x | col3 | col4 | col2_y |
|---|
| 0 | a | d | g | j | 30 |
|---|
| 1 | b | e | h | k | 10 |
|---|
| 2 | c | f | i | l | 20 |
|---|
python
uno.merge(dos,
how='outer',
on = "col1")
| col1 | col2_x | col3_x | col2_y | col3_y |
|---|
| 0 | 1 | 4.0 | 7.0 | NaN | NaN |
|---|
| 1 | 2 | 5.0 | 8.0 | NaN | NaN |
|---|
| 2 | 3 | 6.0 | 9.0 | NaN | NaN |
|---|
| 3 | 10 | NaN | NaN | 14.0 | 18.0 |
|---|
| 4 | 11 | NaN | NaN | 15.0 | 19.0 |
|---|
| 5 | 12 | NaN | NaN | 16.0 | 20.0 |
|---|
| 6 | 13 | NaN | NaN | 17.0 | 21.0 |
|---|
python
uno.merge(dos,
how='outer',
on = "col1",
suffixes=('_dfizq', '_dfder'))
| col1 | col2_dfizq | col3_dfizq | col2_dfder | col3_dfder |
|---|
| 0 | 1 | 4.0 | 7.0 | NaN | NaN |
|---|
| 1 | 2 | 5.0 | 8.0 | NaN | NaN |
|---|
| 2 | 3 | 6.0 | 9.0 | NaN | NaN |
|---|
| 3 | 10 | NaN | NaN | 14.0 | 18.0 |
|---|
| 4 | 11 | NaN | NaN | 15.0 | 19.0 |
|---|
| 5 | 12 | NaN | NaN | 16.0 | 20.0 |
|---|
| 6 | 13 | NaN | NaN | 17.0 | 21.0 |
|---|
python
dos.merge(uno,
on = "col1",
how='left')
| col1 | col2_x | col3_x | col2_y | col3_y |
|---|
| 0 | 10 | 14 | 18 | NaN | NaN |
|---|
| 1 | 11 | 15 | 19 | NaN | NaN |
|---|
| 2 | 12 | 16 | 20 | NaN | NaN |
|---|
| 3 | 13 | 17 | 21 | NaN | NaN |
|---|
python
dos.merge(uno,
on = "col1",
how='right')
| col1 | col2_x | col3_x | col2_y | col3_y |
|---|
| 0 | 1 | NaN | NaN | 4 | 7 |
|---|
| 1 | 2 | NaN | NaN | 5 | 8 |
|---|
| 2 | 3 | NaN | NaN | 6 | 9 |
|---|
python
seis = pd.DataFrame({"c_1": [1,2],
"c_2": ["a", "b"]})
seis
python
uno.merge(seis,
how="inner",
left_on = "col1",
right_on = "c_1")
| col1 | col2 | col3 | c_1 | c_2 |
|---|
| 0 | 1 | 4 | 7 | 1 | a |
|---|
| 1 | 2 | 5 | 8 | 2 | b |
|---|
python
uno.merge(seis,
how="left",
left_on = "col1",
right_on = "c_1")
| col1 | col2 | col3 | c_1 | c_2 |
|---|
| 0 | 1 | 4 | 7 | 1.0 | a |
|---|
| 1 | 2 | 5 | 8 | 2.0 | b |
|---|
| 2 | 3 | 6 | 9 | NaN | NaN |
|---|
python
uno.merge(seis,
how="right",
left_on = "col1",
right_on = "c_1")
| col1 | col2 | col3 | c_1 | c_2 |
|---|
| 0 | 1 | 4 | 7 | 1 | a |
|---|
| 1 | 2 | 5 | 8 | 2 | b |
|---|
python
uno.merge(seis,
how="outer",
left_on = "col1",
right_on = "c_1")
| col1 | col2 | col3 | c_1 | c_2 |
|---|
| 0 | 1 | 4 | 7 | 1.0 | a |
|---|
| 1 | 2 | 5 | 8 | 2.0 | b |
|---|
| 2 | 3 | 6 | 9 | NaN | NaN |
|---|
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
| Evento | Encargado |
|---|
| 2018-01-01 00:00:00 | Id1 | Roberto C. |
|---|
| 2018-01-01 01:00:00 | Id2 | Jorge L. |
|---|
| 2018-01-01 02:00:00 | Id3 | Lucía M. |
|---|
python
| Ubicación | Repetido |
|---|
| 2018-01-01 00:00:00 | ss_2 | True |
|---|
| 2018-01-01 01:00:00 | ss_11 | False |
|---|
| 2018-01-01 02:00:00 | ss_2 | False |
|---|
| 2018-01-01 03:00:00 | ss_6 | True |
|---|
| 2018-01-01 04:00:00 | ss_4 | True |
|---|
python
eventos1.join(eventos2,
how = "left")
| Evento | Encargado | Ubicación | Repetido |
|---|
| 2018-01-01 00:00:00 | Id1 | Roberto C. | ss_2 | True |
|---|
| 2018-01-01 01:00:00 | Id2 | Jorge L. | ss_11 | False |
|---|
| 2018-01-01 02:00:00 | Id3 | Lucía M. | ss_2 | False |
|---|
python
eventos1.join(eventos2,
how = "right")
| Evento | Encargado | Ubicación | Repetido |
|---|
| 2018-01-01 00:00:00 | Id1 | Roberto C. | ss_2 | True |
|---|
| 2018-01-01 01:00:00 | Id2 | Jorge L. | ss_11 | False |
|---|
| 2018-01-01 02:00:00 | Id3 | Lucía M. | ss_2 | False |
|---|
| 2018-01-01 03:00:00 | NaN | NaN | ss_6 | True |
|---|
| 2018-01-01 04:00:00 | NaN | NaN | ss_4 | True |
|---|
python
eventos1.join(eventos2,
how = "inner")
| Evento | Encargado | Ubicación | Repetido |
|---|
| 2018-01-01 00:00:00 | Id1 | Roberto C. | ss_2 | True |
|---|
| 2018-01-01 01:00:00 | Id2 | Jorge L. | ss_11 | False |
|---|
| 2018-01-01 02:00:00 | Id3 | Lucía M. | ss_2 | False |
|---|
python
eventos1.join(eventos2,
how = "outer")
| Evento | Encargado | Ubicación | Repetido |
|---|
| 2018-01-01 00:00:00 | Id1 | Roberto C. | ss_2 | True |
|---|
| 2018-01-01 01:00:00 | Id2 | Jorge L. | ss_11 | False |
|---|
| 2018-01-01 02:00:00 | Id3 | Lucía M. | ss_2 | False |
|---|
| 2018-01-01 03:00:00 | NaN | NaN | ss_6 | True |
|---|
| 2018-01-01 04:00:00 | NaN | NaN | ss_4 | True |
|---|