100% encontró este documento útil (9 votos)
12K vistas

Python For Data Analysis-1-299

Este documento presenta la segunda edición del libro "Python para análisis de datos" por Wes McKinney. El libro explica cómo usar las bibliotecas Python como NumPy, Pandas, Matplotlib, Scipy e IPython/Jupyter para el análisis y visualización de datos. El primer capítulo introduce estas bibliotecas y proporciona instrucciones para instalar Python e implementar un entorno de desarrollo. El segundo capítulo cubre conceptos básicos de Python, IPython y Jupyter Notebooks.

Cargado por

mayra nuñez01
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
100% encontró este documento útil (9 votos)
12K vistas

Python For Data Analysis-1-299

Este documento presenta la segunda edición del libro "Python para análisis de datos" por Wes McKinney. El libro explica cómo usar las bibliotecas Python como NumPy, Pandas, Matplotlib, Scipy e IPython/Jupyter para el análisis y visualización de datos. El primer capítulo introduce estas bibliotecas y proporciona instrucciones para instalar Python e implementar un entorno de desarrollo. El segundo capítulo cubre conceptos básicos de Python, IPython y Jupyter Notebooks.

Cargado por

mayra nuñez01
Derechos de autor
© © All Rights Reserved
Nos tomamos en serio los derechos de los contenidos. Si sospechas que se trata de tu contenido, reclámalo aquí.
Formatos disponibles
Descarga como PDF, TXT o lee en línea desde Scribd
Está en la página 1/ 299

edición

2da  
Machine Translated by Google

pitón  para
Análisis  de  los  datos
LITIGIOS  DE  DATOS  CON  PANDAS,
NUMPY  Y  IPYTHON

energizado  por

wes  mckinney
Machine Translated by Google
Machine Translated by Google

SEGUNDA  EDICION

Python  para  análisis  de  datos
Gestión  de  datos  con  Pandas,  
NumPy  e  IPython

wes  mckinney

Pekín  Boston  Farnham  Sebastopol  Tokio
Machine Translated by Google

Python  para  análisis  de  
datos  por  Wes  McKinney

Derechos  de  autor  ©  2018  William  McKinney.  Reservados  todos  los  derechos.

Impreso  en  los  Estados  Unidos  de  América.

Publicado  por  O'Reilly  Media,  Inc.,  1005  Gravenstein  Highway  North,  Sebastopol,  CA  95472.

Los  libros  de  O'Reilly  se  pueden  comprar  con  fines  educativos,  comerciales  o  de  promoción  de  ventas.  Las  ediciones  en  línea  también  están  
disponibles  para  la  mayoría  de  los  títulos  (http://oreilly.com/safari).  Para  obtener  más  información,  comuníquese  con  nuestro  departamento  de  
ventas  institucionales/corporativas:  800­998­9938  o  [email protected].

Montaje:  Marie  Beaugureau Indexador:  Lucie  Haskins
Editora  de  producción:  Kristen  Brown Diseñador  de  interiores:  David  Futato
Correctora:  Jasmine  Kwityn Diseño  de  portada:  Karen  Montgomery
Correctora:  Rachel  Monaghan Ilustrador:  Rebecca  Demarest

Octubre  2012: Primera  edición
Octubre  2017: Segunda  edicion

Historial  de  revisiones  de  la  segunda  edición

2017­09­25:  Primer  lanzamiento

Consulte  http://oreilly.com/catalog/errata.csp?isbn=9781491957660  para  obtener  detalles  de  la  versión.

El  logotipo  de  O'Reilly  es  una  marca  comercial  registrada  de  O'Reilly  Media,  Inc.  Python  para  análisis  de  datos,  la  imagen  de  portada  y  la  imagen  
comercial  relacionada  son  marcas  comerciales  de  O'Reilly  Media,  Inc.

Si  bien  el  editor  y  el  autor  se  han  esforzado  de  buena  fe  para  garantizar  que  la  información  y  las  instrucciones  contenidas  en  este  trabajo  sean  
precisas,  el  editor  y  el  autor  renuncian  a  toda  responsabilidad  por  errores  u  omisiones,  incluida,  entre  otras,  la  responsabilidad  por  daños  que  
resulten  del  uso  de  o  confianza  en  este  trabajo.  El  uso  de  la  información  e  instrucciones  contenidas  en  este  trabajo  es  bajo  su  propio  riesgo.  Si  
alguna  muestra  de  código  u  otra  tecnología  que  este  trabajo  contiene  o  describe  está  sujeta  a  licencias  de  código  abierto  o  a  los  derechos  de  
propiedad  intelectual  de  otros,  es  su  responsabilidad  asegurarse  de  que  su  uso  cumpla  con  dichas  licencias  y/o  derechos.

978­1­491­95766­0

[LSI]
Machine Translated by Google

Tabla  de  contenido

Prefacio. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . xi

1.  Preliminares. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.1  ¿De  qué  trata  este  libro? 1
¿Qué  tipo  de  datos? 1

1.2  ¿Por  qué  Python  para  el  análisis  de  datos? 2

Python  como   2

pegamento  Resolviendo  el  problema  de  los  "dos   3  

idiomas" ¿Por  qué  no  Python? 3

1.3  Bibliotecas  esenciales  de  Python   4

NumPy   4

pandas   4

matplotlib  
IPython  y  Jupyter  SciPy   5  6

scikit­ 6
learn   7
statsmodels   8

1.4  Instalación  y  configuración   8  
Windows   9

Apple  (OS  X,  macOS) 9
GNU/Linux   9

Instalación  o  actualización  de  paquetes  Python   10

Python  2  y  Python  3  Entornos   11

de  desarrollo  integrados  (IDE)  y  editores  de  texto  1.5  Comunidad  y  conferencias  1.6   11

Navegación  por  este  libro  Ejemplos  de   12

código  Datos  para  ejemplos 12
13
13

iii
Machine Translated by Google

Convenciones  de  importación 14

Jerga 14

2.  Conceptos  básicos  del  lenguaje  Python,  IPython  y  Jupyter  Notebooks. . . . . . . . . . . . . . . . . . . . . . . .  15

2.1  El  intérprete  de  Python  2.2   dieciséis

Conceptos  básicos  de   17

IPython  Ejecución  de  IPython  Shell   17

Ejecución  de  Jupyter  Notebook   18

Finalización  de   21

pestañas   23
Introspección  El  comando   25

%run  Ejecución  de  código  desde  el  portapapeles   26

Atajos  de  teclado  de  terminal  Acerca   27

de  los  comandos  mágicos   28  

Integración  con  Matplotlib   29

2.3  Conceptos  básicos  del  lenguaje   30

Python  Semántica  del   30

lenguaje  Tipos   38
escalares  Flujo  de  control 46

3.  Estructuras  de  datos,  funciones  y  archivos  integrados. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .  51  
3.1  Estructuras  de  datos  y  secuencias   51
Lista   51
de   54  
tuplas  Funciones  de  secuencia   59
incorporadas  dict 61
colocar sesenta  y  cinco

Comprensiones  de  listas,  conjuntos  y  dictados 67
3.2  Funciones 69  

Espacios  de  nombres,  ámbito  y  funciones  locales   70

que  devuelven  valores  múltiples   71

Las  funciones  son  objetos   72

Funciones  anónimas  (Lambda)  Currying:   73

Argumentos  parciales  Generadores  de  aplicaciones   74
Manejo  de   75

errores  y  excepciones  3.3  Archivos  y  el   77

sistema  operativo  Bytes  y  Unicode  con   80

archivos  3.4  Conclusión 83
84

4.  Conceptos  básicos  de  NumPy:  arreglos  y  computación  vectorizada. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .  
85  4.1  NumPy  ndarray:  un  objeto  de  matriz  multidimensional 87

IV  |  Tabla  de  contenido
Machine Translated by Google

Creación  de  ndarrays   88
Tipos  de  datos  para  ndarrays   90
Aritmética  con  NumPy  Arrays   93
Indexación  y  corte  básicos   94  
Indexación  booleana   99
Indexación   102
elegante  Transposición  de  matrices  y  ejes  de   103
intercambio  4.2  Funciones  universales:  funciones  de  matriz  rápidas   105
basadas  en  elementos  4.3  Programación  orientada  a  matrices  con  matrices 108  
Expresión  de  la  lógica  condicional  como  operaciones  con   109
matrices  Métodos  matemáticos  y  estadísticos   111
Métodos  para  matrices  booleanas   113
113
Clasificación  de  conjuntos  únicos   114
y  otros  lógicos  4.4  Entrada  y  salida  de  archivos   115
con  matrices  4.5   116
Álgebra  lineal  4.6  Generación  de  números   118
pseudoaleatorios  4.7  Ejemplo:   119
Paseos  aleatorios  Simulación  de  muchos  paseos  aleatorios  a  la  vez 121
4.8  Conclusión 122

5.  Introducción  a  los  pandas. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .  123  124

5.1  Introducción  a  las  estructuras  de  datos  pandas
Series   124
DataFrame   128
Index  Objects   134
5.2  Funcionalidad  esencial   136
Reindexación   136
Eliminación  de  entradas  de  un  eje   138  
Indexación,  selección  y  filtrado  de  índices   140
enteros  Aritmética   145
y  función  de  alineación  de  datos   146
Aplicación  y  mapeo  Clasificación  y   151
clasificación  de  índices   153  
de  eje  con  etiquetas  duplicadas  5.3   157
Resumen  y  cálculo  de  estadísticas  descriptivas 158
Valores  únicos  de  correlación  y   160
covarianza,  conteo  de  valores  y  membresía  5.4  Conclusión 162
165

6.  Carga  de  datos,  almacenamiento  y  formatos  de  archivo. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .  167

6.1  Leer  y  escribir  datos  en  formato  de  texto 167

Tabla  de  contenido  |  v
Machine Translated by Google

Lectura  de  archivos  de  texto  por   173
partes  Escritura  de  datos  en   175
formato  de  texto  Trabajo  con  formatos   176
delimitados   178  
Datos  JSON  XML  y  HTML:  Web   180
Scraping  6.2  Formatos  de   183
datos  binarios  Uso  del   184
formato  HDF5  Lectura  de  archivos   186
de  Microsoft  Excel  6.3  Interacción   187  
con  API  web  6.4  Interacción  con   188
bases  de  datos  6.5  Conclusión 190

7.  Limpieza  y  preparación  de  datos. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .  191
7.1  Manejo  de  datos  faltantes 191  
Filtrar  datos  faltantes  Completar   193
datos  faltantes  7.2   195
Transformación  de  datos 197
Eliminación  de  duplicados   197
Transformación  de  datos  Uso  de  una  función  o  mapeo   198  
Reemplazo  de   200
valores  Cambio  de  nombre   201
de  índices  de  eje  Discretización   203
y  clasificación  en  intervalos  Detección   205
y  filtrado  de  valores  atípicos  Permutación  y   206  
muestreo  aleatorio  Cómputo  de  indicadores/ 208
variables  ficticias  7.3   211

Manipulación  de  cadenas   211

Métodos  de  objetos  de   213
cadena  Expresiones  regulares  Funciones  de   216  
cadena  vectorizadas  en  pandas  7.4  Conclusión 219

8.  Gestión  de  datos:  unir,  combinar  y  remodelar. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221

8.1  Indexación  jerárquica   221

Reordenación  y  clasificación  de   224

niveles  Estadísticas  resumidas  por   225
nivel  Indexación  con  columnas  de  DataFrame 225
8.2  Combinación  y  fusión  de  conjuntos  de  datos   227
Uniones  de  marcos  de  datos  de  estilo  de   227
base  de  datos  Fusión  en   232  
el  índice  Concatenación  a  lo  largo  de   236
un  eje  Combinación  de  datos  con  superposición 241

8.3  Reformar  y  pivotar 242

vi |  Tabla  de  contenido
Machine Translated by Google

Remodelación  con  indexación  jerárquica   243
Rotación  de  formato  “largo”  a  “ancho”   246
Rotación  de  formato  “ancho”  a  “largo”   249
8.4  Conclusión 251

9.  Trazado  y  Visualización. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .  253  9.1  Breve  
introducción  a  la  API  de  matplotlib  Figuras   253
y  subgráficos  Colores,   255
marcadores  y  estilos  de  línea  Marcas,   259  
etiquetas  y  leyendas  Anotaciones   261
y  dibujos  en  un  subgráfico  Guardar  gráficos  en   265
un  archivo  Matplotlib   267
Configuración  9.2  Representación   268
gráfica  con  pandas  y  seaborn  Gráficos  de   268  
líneas   269
Gráficos   272
de  barras  Histogramas  y  gráficos  de   277
densidad  Dispersión  o   280
Gráficos  de  puntos  Cuadrículas  de   283  
facetas  y  datos  categóricos  9.3  Otras   285
herramientas  de  visualización  de  Python  9.4  Conclusión 286

10.  Agregación  de  Datos  y  Operaciones  de  Grupo. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .  287  10.1  
Mecánica  de  GroupBy  Iteración   288  
sobre  grupos  Selección  de   291
una  columna  o  subconjunto  de  columnas  Agrupación   293
con  dictados  y  series  Agrupación  con   294
funciones  Agrupación  por   295
niveles  de  índice  10.2  Agregación   295  
de  datos 296
Aplicación  por  columnas  y  de  función  múltiple  Devolución   298
de  datos  agregados  sin  índices  de  fila  10.3  Aplicar:  División   301
general­aplicación­combinación  Supresión  de   302
las  claves  de  grupo  Análisis   304  
cuantílico  y  de  cubo  Ejemplo:   305
Relleno  de  valores  faltantes  con  valores  específicos  del  grupo  Ejemplo:   306
Muestreo  aleatorio  y  permutación  Ejemplo:  Grupo   308
ponderado  Ejemplo  de  promedio  y  correlación:  regresión   310
lineal  por  grupos 312  
10.4  Tablas  dinámicas  y  tabulación  cruzada 313
Tabulaciones  cruzadas:  Tabulación   315
cruzada  10.5  Conclusión 316

Tabla  de  contenido  | viii
Machine Translated by Google

11.  Serie  temporal. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .  317

11.1  Herramientas  y  tipos  de  datos  de  fecha  y  hora 318

Conversión  entre  cadena  y  fecha  y  hora  11.2  Conceptos   319
básicos  de  series  temporales 322

Indexación,  selección,  creación  de   323

subconjuntos  de  series  temporales  con  índices   326  

duplicados  11.3  Rangos  de  fechas,  frecuencias  y  desplazamientos 327

Generar  intervalos  de  fechas   328

Frecuencias  y  compensaciones  de   330

fechas  Cambio  de  datos  (adelantados  y  atrasados)   332

11.4  Manejo  de  zonas  horarias   335  
Operaciones  de  localización  y  conversión  de  zonas   335

horarias  con  objetos  de  marcas  de  tiempo  con  reconocimiento  de  zona   338

horaria  Operaciones  entre  diferentes  zonas  horarias  11.5   339
Períodos  y  aritmética  de  períodos  Conversión   339

de  frecuencia  de  períodos  Frecuencias   340  

de  períodos  trimestrales  Conversión  de   342

marcas  de  tiempo  Períodos  (y  viceversa) 344

Creación  de  un  índice  de  período  a  partir  de   345

matrices  11.6  Remuestreo  y  conversión  de  frecuencia   348

Remuestreo  inferior   349  

Remuestreo  superior  e  interpolación   352

Remuestreo  con  períodos  11.7   353

Funciones  de  ventana  móvil 354

Funciones  ponderadas  exponencialmente   358

Funciones  binarias  de  ventana  móvil  Funciones   359  

de  ventana  móvil  definidas  por  el  usuario  11.8  Conclusión 361
362

12.  Pandas  avanzados. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .  363  
12.1  Antecedentes  de   363  

datos  categóricos  y  motivación   363

Tipo  categórico  en  pandas   365

Cálculos  con  categóricos  Métodos   367

categóricos  12.2   370

GroupBy  avanzado  Use   373  

transformaciones  de  grupo  y  GroupBys  "desenvueltos"   373

Remuestreo  de  tiempo   377

agrupado  12.3  Técnicas  para  el   378

encadenamiento  de   380
métodos  El  método  de  tubería  12.4  Conclusión 381

viii |  Tabla  de  contenido
Machine Translated by Google

13.  Introducción  a  las  bibliotecas  de  modelado  en  Python. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .  
383  13.1  Interfaz  entre  pandas  y  el  código  del  modelo   383
13.2  Creación  de  descripciones  de  modelos  con   386
Patsy  Transformaciones  de  datos  en  Patsy   389
Fórmulas  Datos  categóricos  y   390
Patsy  13.3  Introducción  a  statsmodels   393  
Estimación  de  modelos   393
lineales  Estimación  de  procesos  de  series  temporales 396
13.4  Introducción  a  scikit­learn 397

13.5  Continuación  de  su  educación 401

14.  Ejemplos  de  análisis  de  datos. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .  
403  14.1  1.USA.gov  Datos  de  Bitly   403
Conteo  de  zonas  horarias  en  Pure  Python   404
Conteo  de  zonas  horarias  con  pandas 406
14.2  Conjunto  de  datos  MovieLens  1M 413  

Desacuerdo  de  calificación  de  medición 418

14.3  Nombres  de  bebés  de  EE.  UU.  1880– 419

2010  Análisis  de  tendencias  de  nombres 425
14.4  Base  de  datos  de  alimentos  del   434
USDA  14.5  Base  de  datos  de  la  Comisión  Federal  de  Elecciones  de  2012 440  

Estadísticas  de  donaciones  por  ocupación  y  agrupación  de   442

empleadores  Cantidades  de   445

donaciones  Estadísticas  de   447
donaciones  por  estado  14.6  Conclusión 448

A.  NumPy  avanzado. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .  449
A.1  Jerarquía  NumPy  dtype  de   449

elementos  internos  del  objeto   450

ndarray  A.2  Manipulación  avanzada  de  matrices 451

Remodelación  de   452  
arreglos  C  versus  orden   454

Fortran  Concatenación  y  división  de  arreglos   454

Elementos  repetidos:  mosaico  y  repetición   457

Equivalentes  de  indexación  sofisticados:  tomar  y   459

poner  A.5  Matrices   460  

estructuradas  y  de  registro 462
465
466
466
468  
469

Tabla  de  contenido  | ix
Machine Translated by Google

Tipos  anidados  y  campos  multidimensionales  ¿Por  qué   469

utilizar  matrices  estructuradas? 470

A.6  Más  acerca  de  la  clasificación   471

de  clasificaciones  indirectas:  argsort  y  lexsort   472  

Algoritmos  de  clasificación  alternativos   474

Clasificación  parcial  de  matrices   474

numpy.searchsorted:  búsqueda  de  elementos  en  una  matriz  ordenada 475

A.7  Escribir  funciones  NumPy  rápidas  con  Numba 476

Creación  de  objetos  numpy.ufunc  personalizados  con  Numba  A.8   478  

Entrada  y  salida  de  matrices  avanzadas  Archivos   478

asignados  a  la  memoria  HDF5   478

y  otras  opciones  de  almacenamiento  de  matrices  A.9   480

Consejos  de  rendimiento  La   480

importancia  de  la  memoria  contigua 480

B.  Más  información  sobre  el  sistema  IPython. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .  
483  B.1  Uso  del  historial  de  comandos   483

Búsqueda  y  reutilización  de  las  variables  de  entrada  y   483

salida  del  historial  de  comandos   484  

B.2  Interacción  con  el  sistema  operativo  Shell   485
Comandos  y  alias  Directorio   486

Sistema  de  marcadores  B.3   487

Herramientas  de  desarrollo  de  software   487

Depurador  interactivo   488  

Código  de  temporización:  %time  y   492

%timeit  Básico  Generación  de  perfiles:   494

%prun  y  %run  ­p  Generación  de  perfiles  de  una  función  línea  por  línea 496

B.4  Sugerencias  para  el  desarrollo  de  código  productivo  usando  IPython 498

Recarga  de  módulos  Dependencias   498  

Sugerencias  de  diseño   499

de  código  B.5  Funciones  avanzadas  de   500

IPython  Creación  de  sus  propias  clases  Perfiles  y   500

configuración  compatibles  con   501
IPython  B.6  Conclusión 503

Índice. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .  505

x  |  Tabla  de  contenido
Machine Translated by Google

Prefacio

Nuevo  para  la  Segunda  Edición
La  primera  edición  de  este  libro  se  publicó  en  2012,  durante  una  época  en  la  que  las  bibliotecas  de  análisis  de  
datos  de  código  abierto  para  Python  (como  pandas)  eran  muy  nuevas  y  se  desarrollaban  rápidamente.  En  esta  
segunda  edición  actualizada  y  ampliada,  revisé  los  capítulos  para  tener  en  cuenta  tanto  los  cambios  incompatibles  
y  las  obsolescencias  como  las  nuevas  características  que  se  produjeron  en  los  últimos  cinco  años.  También  
agregué  contenido  nuevo  para  presentar  herramientas  que  no  existían  en  2012  o  que  no  habían  madurado  lo  
suficiente  como  para  hacer  el  primer  corte.
Finalmente,  he  tratado  de  evitar  escribir  sobre  proyectos  de  código  abierto  nuevos  o  de  vanguardia  que  pueden  
no  haber  tenido  la  oportunidad  de  madurar.  Me  gustaría  que  los  lectores  de  esta  edición  descubran  que  el  
contenido  sigue  siendo  casi  tan  relevante  en  2020  o  2021  como  lo  es  en  2017.

Las  principales  actualizaciones  de  esta  segunda  edición  incluyen:

•  Todo  el  código,  incluido  el  tutorial  de  Python,  actualizado  para  Python  3.6  (la  primera  edición
usó  Python  2.7)

•  Instrucciones  de  instalación  de  Python  actualizadas  para  Anaconda  Python  Distribution  y  otros  paquetes  de  
Python  necesarios

•  Actualizaciones  para  las  últimas  versiones  de  la  biblioteca  pandas  en  2017  •  Un  

nuevo  capítulo  sobre  algunas  herramientas  pandas  más  avanzadas  y  algunos  otros  consejos  de  uso  •  Una  

breve  introducción  al  uso  de  statsmodels  y  scikit­learn

También  reorganicé  una  parte  importante  del  contenido  de  la  primera  edición  para  que  el  libro  fuera  más  accesible  
para  los  recién  llegados.

xi
Machine Translated by Google

Las  convenciones  usadas  en  este  libro

En  este  libro  se  utilizan  las  siguientes  convenciones  tipográficas:

Cursiva  

Indica  nuevos  términos,  URL,  direcciones  de  correo  electrónico,  nombres  de  archivo  y  extensiones  de  archivo.

Ancho  constante  Se  

utiliza  para  listas  de  programas,  así  como  dentro  de  párrafos  para  referirse  a  elementos  de  programas  
como  nombres  de  variables  o  funciones,  bases  de  datos,  tipos  de  datos,  variables  de  entorno,  
declaraciones  y  palabras  clave.

Negrita  de  ancho  constante  

Muestra  comandos  u  otro  texto  que  el  usuario  debe  escribir  literalmente.

Cursiva  de  ancho  constante
Muestra  texto  que  debe  ser  reemplazado  con  valores  proporcionados  por  el  usuario  o  por  valores  
determinados  por  el  contexto.

Este  elemento  significa  un  consejo  o  sugerencia.

Este  elemento  significa  una  nota  general.

Este  elemento  indica  una  advertencia  o  precaución.

Uso  de  ejemplos  de  código
Puede  encontrar  archivos  de  datos  y  material  relacionado  para  cada  capítulo  en  el  repositorio  de  GitHub  de  
este  libro  en  http://github.com/wesm/pydata­book.

Este  libro  está  aquí  para  ayudarle  a  hacer  su  trabajo.  En  general,  si  se  ofrece  un  código  de  ejemplo  con  este  
libro,  puede  usarlo  en  sus  programas  y  documentación.  No  es  necesario  que  se  comunique  con  nosotros  para  
obtener  permiso  a  menos  que  esté  reproduciendo  una  parte  significativa  del  código.  Por  ejemplo,  escribir  un  
programa  que  use  varios  fragmentos  de  código  de  este

xi |  Prefacio
Machine Translated by Google

libro  no  requiere  permiso.  Vender  o  distribuir  un  CD­ROM  de  ejemplos  de  libros  de  O'Reilly  requiere  
permiso.  Responder  una  pregunta  citando  este  libro  y  citando  código  de  ejemplo  no  requiere  
permiso.  La  incorporación  de  una  cantidad  significativa  de  código  de  ejemplo  de  este  libro  en  la  
documentación  de  su  producto  requiere  permiso.

Apreciamos,  pero  no  requerimos,  atribución.  Una  atribución  suele  incluir  el  título,  el  autor,  el  editor  
y  el  ISBN.  Por  ejemplo:  “Python  para  análisis  de  datos  por  Wes  McKinney  (O'Reilly).  Derechos  de  
autor  2017  Wes  McKinney,  978­1­491­95766­0.”

Si  cree  que  su  uso  de  los  ejemplos  de  código  está  fuera  del  uso  justo  o  del  permiso  otorgado  
anteriormente,  no  dude  en  contactarnos  en  [email protected].

Safari  O´Reilly
Safari  (anteriormente  Safari  Books  Online)  es  una  plataforma  de  
referencia  y  capacitación  basada  en  membresía  para  empresas,  
gobiernos,  educadores  e  individuos.

Los  miembros  tienen  acceso  a  miles  de  libros,  videos  de  capacitación,  rutas  de  aprendizaje,  
tutoriales  interactivos  y  listas  de  reproducción  seleccionadas  de  más  de  250  editores,  incluidos  
O'Reilly  Media,  Harvard  Business  Review,  Prentice  Hall  Professional,  Addison­Wesley  Professional,  
Microsoft  Press ,  Sams,  Que,  Peachpit  Press,  Adobe,  Focal  Press,  Cisco  Press,  John  Wiley  &  
Sons,  Syngress,  Morgan  Kaufmann,  IBM  Redbooks,  Packt,  Adobe  Press,  FT  Press,  Apress,  
Manning,  New  Riders,  McGraw­Hill,  Jones  &  Bartlett  y  Course  Technology,  entre  otros.

Para  obtener  más  información,  visite  http://oreilly.com/safari.

Cómo  contactarnos
Dirija  sus  comentarios  y  preguntas  sobre  este  libro  a  la  editorial:

O'Reilly  Media,  Inc.
1005  Gravenstein  Highway  North  
Sebastopol,  CA  95472  
800­998­9938  (en  los  Estados  Unidos  o  Canadá)  
707­829­0515  (internacional  o  local)  
707­829­0104  (fax)

Tenemos  una  página  web  para  este  libro,  donde  enumeramos  las  erratas,  ejemplos  y  cualquier  
información  adicional.  Puede  acceder  a  esta  página  en  http://bit.ly/python_data_analysis_2e.

Prefacio  | XIII
Machine Translated by Google

Para  comentar  o  hacer  preguntas  técnicas  sobre  este  libro,  envíe  un  correo  electrónico  a  bookques
[email protected].

Para  obtener  más  información  sobre  nuestros  libros,  cursos,  conferencias  y  noticias,  visite  nuestro  
sitio  web  en  http://www.oreilly.com.

Encuéntrenos  en  Facebook:  http://facebook.com/oreilly  

Síganos  en  Twitter:  http://twitter.com/oreillymedia

Míranos  en  YouTube:  http://www.youtube.com/oreillymedia

Expresiones  de  gratitud
Este  trabajo  es  el  producto  de  muchos  años  de  debates  fructíferos,  colaboraciones  y  asistencia  con  
y  de  muchas  personas  en  todo  el  mundo.  Me  gustaría  agradecer  a  algunos  de  ellos.

En  memoria:  John  D.  Hunter  (1968­2012)
Nuestro  querido  amigo  y  colega  John  D.  Hunter  falleció  después  de  una  batalla  contra  el  cáncer  de  
colon  el  28  de  agosto  de  2012.  Esto  fue  poco  tiempo  después  de  haber  completado  el  manuscrito  
final  de  la  primera  edición  de  este  libro.

Sería  difícil  exagerar  el  impacto  y  el  legado  de  John  en  las  comunidades  científicas  y  de  datos  de  
Python.  Además  de  desarrollar  matplotlib  a  principios  de  la  década  de  2000  (una  época  en  la  que  
Python  no  era  tan  popular),  ayudó  a  dar  forma  a  la  cultura  de  una  generación  crítica  de  
desarrolladores  de  código  abierto  que  se  han  convertido  en  pilares  del  ecosistema  de  Python  que  
ahora  solemos  tomar.  por  sentado

Tuve  la  suerte  de  conectarme  con  John  al  principio  de  mi  carrera  de  código  abierto  en  enero  de  
2010,  justo  después  de  lanzar  pandas  0.1.  Su  inspiración  y  tutoría  me  ayudaron  a  seguir  adelante,  
incluso  en  los  momentos  más  oscuros,  con  mi  visión  de  pandas  y  Python  como  un  lenguaje  de  
análisis  de  datos  de  primera  clase.

John  fue  muy  cercano  a  Fernando  Pérez  y  Brian  Granger,  pioneros  de  IPython,  Jupyter  y  muchas  
otras  iniciativas  en  la  comunidad  de  Python.  Teníamos  la  esperanza  de  trabajar  en  un  libro  juntos,  
los  cuatro,  pero  terminé  siendo  yo  el  que  tenía  más  tiempo  libre.  Estoy  seguro  de  que  estaría  
orgulloso  de  lo  que  hemos  logrado,  como  individuos  y  como  comunidad,  en  los  últimos  cinco  años.

Agradecimientos  a  la  Segunda  Edición  (2017)
Han  pasado  casi  cinco  años  desde  que  completé  el  manuscrito  de  la  primera  edición  de  este  libro  
en  julio  de  2012.  Mucho  ha  cambiado.  La  comunidad  de  Python  ha  crecido  enormemente  y  el  
ecosistema  de  software  de  código  abierto  que  la  rodea  ha  florecido.

xiv  |  Prefacio
Machine Translated by Google

Esta  nueva  edición  del  libro  no  existiría  si  no  fuera  por  los  esfuerzos  incansables  de  los  
desarrolladores  principales  de  pandas,  que  han  convertido  el  proyecto  y  su  comunidad  de  usuarios  
en  una  de  las  piedras  angulares  del  ecosistema  de  ciencia  de  datos  de  Python.  Estos  incluyen,  entre  
otros,  Tom  Augspurger,  Joris  van  den  Bossche,  Chris  Bartak,  Phillip  Cloud,  gfyoung,  Andy  Hayden,  
Masaaki  Horikoshi,  Stephan  Hoyer,  Adam  Klein,  Wouter  Overmeire,  Jeff  Reback,  Chang  She,  Skipper  
Seabold,  Jeff  Tratner,  y  sí.

En  cuanto  a  la  redacción  de  esta  segunda  edición,  me  gustaría  agradecer  al  personal  de  O'Reilly  que  
me  ayudó  pacientemente  con  el  proceso  de  redacción.  Esto  incluye  a  Marie  Beaugureau,  Ben  Lorica  
y  Colleen  Toporek.  Nuevamente  tuve  destacados  revisores  técnicos  con  la  contribución  de  Tom  
Augpurger,  Paul  Barry,  Hugh  Brown,  Jonathan  Coe  y  Andreas  Müller.  Gracias.

La  primera  edición  de  este  libro  ha  sido  traducida  a  muchos  idiomas  extranjeros,  incluidos  chino,  
francés,  alemán,  japonés,  coreano  y  ruso.  Traducir  todo  este  contenido  y  ponerlo  a  disposición  de  
un  público  más  amplio  es  un  esfuerzo  enorme  y,  a  menudo,  ingrato.
Gracias  por  ayudar  a  más  personas  en  el  mundo  a  aprender  a  programar  y  utilizar  herramientas  de  
análisis  de  datos.

También  tengo  la  suerte  de  haber  contado  con  el  apoyo  de  Cloudera  y  Two  Sigma  Investments  para  
mis  continuos  esfuerzos  de  desarrollo  de  código  abierto  durante  los  últimos  años.  Con  los  proyectos  
de  software  de  código  abierto  con  menos  recursos  que  nunca  en  relación  con  el  tamaño  de  las  bases  
de  usuarios,  se  está  volviendo  cada  vez  más  importante  para  las  empresas  brindar  apoyo  para  el  
desarrollo  de  proyectos  clave  de  código  abierto.  Es  la  cosa  justa  que  hacer.

Agradecimientos  a  la  Primera  Edición  (2012)
Habría  sido  difícil  para  mí  escribir  este  libro  sin  el  apoyo  de  un  gran  número  de  personas.

En  el  equipo  de  O'Reilly,  estoy  muy  agradecido  por  mis  editores,  Meghan  Blanchette  y  Julie  Steele,  
quienes  me  guiaron  a  través  del  proceso.  Mike  Loukides  también  trabajó  conmigo  en  las  etapas  de  
la  propuesta  y  me  ayudó  a  hacer  realidad  el  libro.

Recibí  una  gran  cantidad  de  revisión  técnica  de  un  gran  elenco  de  personajes.  En  particular,  Martin  
Blais  y  Hugh  Brown  fueron  increíblemente  útiles  para  mejorar  los  ejemplos,  la  claridad  y  la  
organización  del  libro  de  principio  a  fin.  James  Long,  Drew  Conway,  Fernando  Pérez,  Brian  Granger,  
Thomas  Kluyver,  Adam  Klein,  Josh  Klein,  Chang  She  y  Stéfan  van  der  Walt  revisaron  cada  uno  uno  
o  más  capítulos,  brindando  comentarios  puntuales  desde  muchas  perspectivas  diferentes.

Recibí  muchas  ideas  geniales  para  ejemplos  y  conjuntos  de  datos  de  amigos  y  colegas  en  la  
comunidad  de  datos,  entre  ellos:  Mike  Dewar,  Jeff  Hammerbacher,  James  Johndrow,  Kristian  Lum,  
Adam  Klein,  Hilary  Mason,  Chang  She  y  Ashley  Williams.

Prefacio  |  XV
Machine Translated by Google

Por  supuesto,  estoy  en  deuda  con  los  muchos  líderes  de  la  comunidad  científica  de  código  
abierto  de  Python  que  sentaron  las  bases  para  mi  trabajo  de  desarrollo  y  me  alentaron  
mientras  escribía  este  libro:  el  equipo  central  de  IPython  (Fernando  Pérez,  Brian  Granger,  Min  
Ragan­Kelly,  Thomas  Kluyver  y  otros),  John  Hunter,  Skipper  Seabold,  Travis  Oliphant,  Peter  
Wang,  Eric  Jones,  Robert  Kern,  Josef  Perktold,  Francesc  Alted,  Chris  Fonnesbeck  y  muchos  
otros  para  mencionarlos.  Varias  otras  personas  brindaron  una  gran  cantidad  de  apoyo,  ideas  
y  aliento  a  lo  largo  del  camino:  Drew  Conway,  Sean  Taylor,  Giuseppe  Paleologo,  Jared  
Lander,  David  Epstein,  John  Krowas,  Joshua  Bloom,  Den  Pilsworth,  John  Myles­White ,  y  
muchos  otros  que  he  olvidado.

También  me  gustaría  agradecer  a  varias  personas  de  mis  años  de  formación.  Primero,  mis  
antiguos  colegas  de  AQR  que  me  animaron  en  mi  trabajo  con  pandas  a  lo  largo  de  los  años:  
Alex  Reyfman,  Michael  Wong,  Tim  Sargen,  Oktay  Kurbanov,  Matthew  Tschantz,  Roni  Israelov,  
Michael  Katz,  Chris  Uga,  Prasad  Ramanan,  Ted  Square  y  Hoon  Kim.  Por  último,  mis  asesores  
académicos  Haynes  Miller  (MIT)  y  Mike  West  (Duke).

Recibí  una  ayuda  significativa  de  Phillip  Cloud  y  Joris  Van  den  Bossche  en  2014  para  
actualizar  los  ejemplos  de  código  del  libro  y  corregir  algunas  otras  imprecisiones  debido  a  
cambios  en  pandas.

En  el  aspecto  personal,  Casey  me  brindó  un  apoyo  invaluable  en  el  día  a  día  durante  el  
proceso  de  escritura,  tolerando  mis  altibajos  mientras  preparaba  el  borrador  final  además  de  
un  programa  ya  sobrecargado.  Por  último,  mis  padres,  Bill  y  Kim,  me  enseñaron  a  seguir  
siempre  mis  sueños  y  nunca  conformarme  con  menos.

xi  |  Prefacio
Machine Translated by Google

CAPÍTULO  1

Preliminares

1.1  ¿De  qué  trata  este  libro?
Este  libro  se  ocupa  de  los  aspectos  básicos  de  la  manipulación,  el  procesamiento,  la  limpieza  y  el  procesamiento  de  datos  
en  Python.  Mi  objetivo  es  ofrecer  una  guía  de  las  partes  del  lenguaje  de  programación  Python  y  su  ecosistema  de  biblioteca  
orientada  a  datos  y  herramientas  que  lo  equiparán  para  convertirse  en  un  analista  de  datos  efectivo.  Si  bien  el  "análisis  de  
datos"  se  encuentra  en  el  título  del  libro,  el  enfoque  se  centra  específicamente  en  la  programación,  las  bibliotecas  y  las  
herramientas  de  Python  en  lugar  de  la  metodología  de  análisis  de  datos.  Esta  es  la  programación  de  Python  que  necesita  
para  el  análisis  de  datos.

¿Qué  tipo  de  datos?
Cuando  digo  "datos",  ¿a  qué  me  refiero  exactamente?  El  enfoque  principal  está  en  los  datos  estructurados,  un  término  
deliberadamente  vago  que  abarca  muchas  formas  comunes  diferentes  de  datos,  como:

•  Datos  tabulares  o  similares  a  una  hoja  de  cálculo  en  los  que  cada  columna  puede  ser  de  un  tipo  diferente  (cadena,  
numérico,  fecha  u  otro).  Esto  incluye  la  mayoría  de  los  tipos  de  datos  comúnmente  almacenados  en  bases  de  datos  
relacionales  o  archivos  de  texto  delimitados  por  tabulaciones  o  comas.

•  Arrays  multidimensionales  (matrices).  •  Múltiples  

tablas  de  datos  interrelacionados  por  columnas  clave  (lo  que  sería  principal  o
claves  foráneas  para  un  usuario  de  SQL).  

•  Series  de  tiempo  uniforme  o  desigualmente  espaciadas.

Esto  no  es  de  ninguna  manera  una  lista  completa.  Aunque  no  siempre  sea  obvio,  un  gran  porcentaje  de  conjuntos  de  datos  
se  puede  transformar  en  una  forma  estructurada  que  sea  más  adecuada  para  el  análisis  y  el  modelado.  De  lo  contrario,  es  
posible  extraer  características  de  un  conjunto  de  datos.

1
Machine Translated by Google

en  una  forma  estructurada.  Como  ejemplo,  una  colección  de  artículos  de  noticias  podría  procesarse  en  una  
tabla  de  frecuencia  de  palabras,  que  luego  podría  usarse  para  realizar  un  análisis  de  sentimiento.

La  mayoría  de  los  usuarios  de  programas  de  hojas  de  cálculo  como  Microsoft  Excel,  quizás  la  herramienta  
de  análisis  de  datos  más  utilizada  en  el  mundo,  no  serán  ajenos  a  este  tipo  de  datos.

1.2  ¿Por  qué  Python  para  el  análisis  de  datos?

Para  muchas  personas,  el  lenguaje  de  programación  Python  tiene  un  gran  atractivo.  Desde  su  primera  
aparición  en  1991,  Python  se  ha  convertido  en  uno  de  los  lenguajes  de  programación  interpretados  más  
populares,  junto  con  Perl,  Ruby  y  otros.  Python  y  Ruby  se  han  vuelto  especialmente  populares  desde  2005  
aproximadamente  para  crear  sitios  web  utilizando  sus  numerosos  marcos  web,  como  Rails  (Ruby)  y  Django  
(Python).  Dichos  lenguajes  a  menudo  se  denominan  lenguajes  de  secuencias  de  comandos,  ya  que  se  
pueden  usar  para  escribir  rápidamente  pequeños  programas  o  secuencias  de  comandos  para  automatizar  
otras  tareas.  No  me  gusta  el  término  "lenguaje  de  secuencias  de  comandos",  ya  que  conlleva  la  connotación  
de  que  no  se  pueden  usar  para  crear  software  serio.  Entre  los  lenguajes  interpretados,  por  varias  razones  
históricas  y  culturales,  Python  ha  desarrollado  una  comunidad  de  análisis  de  datos  y  computación  científica  
grande  y  activa.  En  los  últimos  10  años,  Python  ha  pasado  de  ser  un  lenguaje  informático  científico  de  
vanguardia  o  “bajo  su  propio  riesgo”  a  uno  de  los  lenguajes  más  importantes  para  la  ciencia  de  datos,  el  
aprendizaje  automático  y  el  desarrollo  de  software  en  general  en  la  academia  y  la  industria.

Para  el  análisis  de  datos  y  la  computación  interactiva  y  la  visualización  de  datos,  Python  inevitablemente  
establecerá  comparaciones  con  otros  lenguajes  de  programación  comercial  y  de  código  abierto  y  herramientas  
de  amplio  uso,  como  R,  MATLAB,  SAS,  Stata  y  otros.  En  los  últimos  años,  el  soporte  mejorado  de  Python  
para  bibliotecas  (como  pandas  y  scikit­learn)  lo  ha  convertido  en  una  opción  popular  para  las  tareas  de  
análisis  de  datos.  Combinado  con  la  fortaleza  general  de  Python  para  la  ingeniería  de  software  de  propósito  
general,  es  una  excelente  opción  como  lenguaje  principal  para  crear  aplicaciones  de  datos.

Python  como  

pegamento  Parte  del  éxito  de  Python  en  la  computación  científica  es  la  facilidad  de  integrar  código  C,  C++  y  
FORTRAN.  La  mayoría  de  los  entornos  informáticos  modernos  comparten  un  conjunto  similar  de  bibliotecas  
FORTRAN  y  C  heredadas  para  realizar  álgebra  lineal,  optimización,  integración,  transformadas  rápidas  de  
Fourier  y  otros  algoritmos  similares.  La  misma  historia  ha  sido  válida  para  muchas  empresas  y  laboratorios  
nacionales  que  han  utilizado  Python  para  unir  décadas  de  software  heredado.

Muchos  programas  consisten  en  pequeñas  porciones  de  código  en  las  que  se  gasta  la  mayor  parte  del  
tiempo,  con  grandes  cantidades  de  "código  adhesivo"  que  no  se  ejecuta  con  frecuencia.  En  muchos  casos,  
el  tiempo  de  ejecución  del  código  adhesivo  es  insignificante;  el  esfuerzo  se  invierte  más  fructíferamente  en  optimizar

2  |  Capítulo  1:  Preliminares
Machine Translated by Google

los  cuellos  de  botella  computacionales,  a  veces  moviendo  el  código  a  un  lenguaje  de  nivel  inferior  como  C.

Resolviendo  el  problema  de  los  "dos  idiomas"  En  muchas  

organizaciones,  es  común  investigar,  crear  prototipos  y  probar  nuevas  ideas  usando  un  lenguaje  de  
computación  más  especializado  como  SAS  o  R  y  luego  trasladar  esas  ideas  para  que  formen  parte  de  un  
sistema  de  producción  más  grande  escrito  en ,  digamos,  Java,  C#  o  C++.  Lo  que  la  gente  encuentra  cada  
vez  más  es  que  Python  es  un  lenguaje  adecuado  no  solo  para  investigar  y  crear  prototipos,  sino  también  
para  construir  los  sistemas  de  producción.  ¿Por  qué  mantener  dos  entornos  de  desarrollo  cuando  uno  es  
suficiente?  Creo  que  cada  vez  más  empresas  seguirán  este  camino,  ya  que  a  menudo  hay  beneficios  
organizacionales  significativos  al  tener  tanto  a  investigadores  como  a  ingenieros  de  software  usando  el  
mismo  conjunto  de  herramientas  de  programación.

¿Por  qué  no  Python?

Si  bien  Python  es  un  entorno  excelente  para  crear  muchos  tipos  de  aplicaciones  analíticas  y  sistemas  de  
uso  general,  hay  una  serie  de  usos  para  los  que  Python  puede  ser  menos  adecuado.

Como  Python  es  un  lenguaje  de  programación  interpretado,  en  general,  la  mayoría  del  código  de  Python  
se  ejecutará  considerablemente  más  lento  que  el  código  escrito  en  un  lenguaje  compilado  como  Java  o  C++.
Como  el  tiempo  del  programador  suele  ser  más  valioso  que  el  tiempo  de  la  CPU,  muchos  están  felices  de  
hacer  este  intercambio.  Sin  embargo,  en  una  aplicación  con  una  latencia  muy  baja  o  requisitos  exigentes  
de  utilización  de  recursos  (por  ejemplo,  un  sistema  comercial  de  alta  frecuencia),  el  tiempo  dedicado  a  
programar  en  un  lenguaje  de  nivel  inferior  (pero  también  de  menor  productividad)  como  C++  para  lograr  el  
máximo  rendimiento  posible  podría  ser  tiempo  bien  invertido.

Python  puede  ser  un  lenguaje  desafiante  para  crear  aplicaciones  de  subprocesos  múltiples  altamente  
concurrentes,  particularmente  aplicaciones  con  muchos  subprocesos  vinculados  a  la  CPU.  La  razón  de  
esto  es  que  tiene  lo  que  se  conoce  como  bloqueo  global  del  intérprete  (GIL),  un  mecanismo  que  evita  que  
el  intérprete  ejecute  más  de  una  instrucción  de  Python  a  la  vez.
Las  razones  técnicas  de  por  qué  existe  el  GIL  están  más  allá  del  alcance  de  este  libro.  Si  bien  es  cierto  
que  en  muchas  aplicaciones  de  procesamiento  de  big  data,  se  puede  requerir  un  grupo  de  computadoras  
para  procesar  un  conjunto  de  datos  en  una  cantidad  de  tiempo  razonable,  todavía  hay  situaciones  en  las  
que  es  deseable  un  sistema  de  proceso  único  y  multiproceso.

Esto  no  quiere  decir  que  Python  no  pueda  ejecutar  código  paralelo  verdaderamente  multiproceso.
Las  extensiones  de  Python  C  que  usan  subprocesos  múltiples  nativos  (en  C  o  C++)  pueden  ejecutar  código  
en  paralelo  sin  verse  afectadas  por  la  GIL,  siempre  que  no  necesiten  interactuar  regularmente  con  objetos  
de  Python.

1.2  ¿Por  qué  Python  para  el  análisis  de  datos?  |  3
Machine Translated by Google

1.3  Bibliotecas  esenciales  de  Python
Para  aquellos  que  están  menos  familiarizados  con  el  ecosistema  de  datos  de  Python  y  las  bibliotecas  
utilizadas  a  lo  largo  del  libro,  daré  una  breve  descripción  general  de  algunas  de  ellas.

NumPy  

NumPy,  abreviatura  de  Numerical  Python,  ha  sido  durante  mucho  tiempo  la  piedra  angular  de  la  computación  
numérica  en  Python.  Proporciona  las  estructuras  de  datos,  los  algoritmos  y  el  pegamento  de  biblioteca  
necesarios  para  la  mayoría  de  las  aplicaciones  científicas  que  involucran  datos  numéricos  en  Python.  
NumPy  contiene,  entre  otras  cosas:

•  Un  objeto  de  matriz  multidimensional  rápido  y  eficiente  ndarray  •  Funciones  

para  realizar  cálculos  de  elementos  con  matrices  o  matemáticas
operaciones  cal  entre  arreglos

•  Herramientas  para  leer  y  escribir  conjuntos  de  datos  basados  en  matrices  

en  el  disco  •  Operaciones  de  álgebra  lineal,  transformada  de  Fourier  y  generación  de  números  

aleatorios  •  Una  API  de  C  madura  para  permitir  el  acceso  a  extensiones  de  Python  y  código  C  o  C++  nativo
Estructuras  de  datos  y  facilidades  computacionales  de  NumPy

Más  allá  de  las  capacidades  de  procesamiento  rápido  de  arreglos  que  NumPy  agrega  a  Python,  uno  de  sus  
usos  principales  en  el  análisis  de  datos  es  como  un  contenedor  para  que  los  datos  pasen  entre  algoritmos  
y  bibliotecas.  Para  datos  numéricos,  las  matrices  NumPy  son  más  eficientes  para  almacenar  y  manipular  
datos  que  las  otras  estructuras  de  datos  integradas  de  Python.  Además,  las  bibliotecas  escritas  en  un  
lenguaje  de  nivel  inferior,  como  C  o  Fortran,  pueden  operar  con  los  datos  almacenados  en  una  matriz  
NumPy  sin  copiar  los  datos  en  alguna  otra  representación  de  memoria.
Por  lo  tanto,  muchas  herramientas  de  computación  numérica  para  Python  asumen  arreglos  NumPy  como  
una  estructura  de  datos  principal  o  apuntan  a  una  interoperabilidad  perfecta  con  NumPy.

pandas  

pandas  proporciona  funciones  y  estructuras  de  datos  de  alto  nivel  diseñadas  para  hacer  que  trabajar  con  
datos  estructurados  o  tabulares  sea  rápido,  fácil  y  expresivo.  Desde  su  aparición  en  2010,  ha  ayudado  a  
que  Python  sea  un  entorno  de  análisis  de  datos  poderoso  y  productivo.  Los  objetos  principales  en  pandas  
que  se  utilizarán  en  este  libro  son  DataFrame,  una  estructura  de  datos  tabular  orientada  a  columnas  con  
etiquetas  de  fila  y  columna,  y  Series ,  un  objeto  de  matriz  etiquetado  unidimensional.  pandas  combina  las  
ideas  de  computación  de  matriz  de  alto  rendimiento  de  

NumPy  con  las  capacidades  flexibles  de  manipulación  de  datos  de  las  hojas  de  cálculo  y  las  bases  de  
datos  relacionales  (como  SQL).  Proporciona  una  funcionalidad  de  indexación  sofisticada  para  facilitar  la  
remodelación,  el  corte  y  el  corte,  la  realización  de  agregaciones  y  la  selección  de  subconjuntos  de  datos.  
Desde  la  manipulación  de  datos,

4  |  Capítulo  1:  Preliminares
Machine Translated by Google

la  preparación  y  la  limpieza  son  una  habilidad  tan  importante  en  el  análisis  de  datos  que  los  pandas  son  uno  de  
los  enfoques  principales  de  este  libro.

Como  antecedentes,  comencé  a  construir  pandas  a  principios  de  2008  durante  mi  mandato  en  AQR  Capital  
Management,  una  empresa  de  gestión  de  inversiones  cuantitativas.  En  ese  momento,  tenía  un  conjunto  distinto  
de  requisitos  que  no  estaban  bien  abordados  por  ninguna  herramienta  única  a  mi  disposición:

•  Estructuras  de  datos  con  ejes  etiquetados  que  admiten  la  alineación  automática  o  explícita  de  datos:  esto  
evita  errores  comunes  que  resultan  de  datos  desalineados  y  trabajar  con  datos  indexados  de  manera  
diferente  que  provienen  de  diferentes  fuentes.

•  Funcionalidad  integrada  de  series  de  tiempo  •  
Las  mismas  estructuras  de  datos  manejan  datos  de  series  de  tiempo  y  datos  que  no  son  de  series  de  tiempo

•  Operaciones  aritméticas  y  reducciones  que  preservan  metadatos  •  Manejo  flexible  

de  datos  faltantes  •  Fusión  y  otras  operaciones  

relacionales  que  se  encuentran  en  bases  de  datos  populares  (basadas  en  SQL,
Por  ejemplo)

Quería  poder  hacer  todas  estas  cosas  en  un  solo  lugar,  preferiblemente  en  un  lenguaje  adecuado  para  el  
desarrollo  de  software  de  propósito  general.  Python  era  un  buen  lenguaje  candidato  para  esto,  pero  en  ese  
momento  no  había  un  conjunto  integrado  de  estructuras  de  datos  y  herramientas  que  proporcionaran  esta  
funcionalidad.  Como  resultado  de  haber  sido  construido  inicialmente  para  resolver  problemas  financieros  y  de  
análisis  de  negocios,  pandas  presenta  una  funcionalidad  de  serie  de  tiempo  especialmente  profunda  y  
herramientas  muy  adecuadas  para  trabajar  con  datos  indexados  en  el  tiempo  generados  por  procesos  de  
negocios.

Para  los  usuarios  del  lenguaje  R  para  computación  estadística,  el  nombre  de  DataFrame  les  resultará  familiar,  
ya  que  el  objeto  recibió  el  nombre  del  objeto  R  data.frame  similar .  A  diferencia  de  Python,  los  marcos  de  datos  
están  integrados  en  el  lenguaje  de  programación  R  y  su  biblioteca  estándar.  Como  resultado,  muchas  funciones  
que  se  encuentran  en  pandas  suelen  ser  parte  de  la  implementación  central  de  R  o  proporcionadas  por  paquetes  
complementarios.

El  nombre  de  pandas  en  sí  se  deriva  de  datos  de  panel,  un  término  econométrico  para  conjuntos  de  datos  
estructurados  multidimensionales,  y  un  juego  con  la  frase  Análisis  de  datos  de  Python  en  sí.

matplotlib  

matplotlib  es  la  biblioteca  de  Python  más  popular  para  producir  gráficos  y  otras  visualizaciones  de  datos  
bidimensionales.  Originalmente  fue  creado  por  John  D.  Hunter  y  ahora  lo  mantiene  un  gran  equipo  de  
desarrolladores.  Está  diseñado  para  crear  tramas  adecuadas  para  su  publicación.  Si  bien  existen  otras  bibliotecas  
de  visualización  disponibles  para  los  programadores  de  Python,  matplotlib  es  la  más  utilizada  y,  como  tal,  
generalmente  tiene  una  buena  inter

1.3  Bibliotecas  esenciales  de  Python  |  5
Machine Translated by Google

gratificación  con  el  resto  del  ecosistema.  Creo  que  es  una  opción  segura  como  herramienta  de  visualización  
predeterminada.

IPython  y  Jupyter  El  proyecto  

IPython  comenzó  en  2001  como  un  proyecto  paralelo  de  Fernando  Pérez  para  crear  un  mejor  intérprete  interactivo  
de  Python.  En  los  siguientes  16  años  se  ha  convertido  en  una  de  las  herramientas  más  importantes  en  la  pila  de  
datos  moderna  de  Python.  Si  bien  no  proporciona  ninguna  herramienta  informática  o  de  análisis  de  datos  por  sí  
mismo,  IPython  está  diseñado  desde  cero  para  maximizar  su  productividad  tanto  en  la  informática  interactiva  
como  en  el  desarrollo  de  software.  Fomenta  un  flujo  de  trabajo  de  ejecución  y  exploración  en  lugar  del  típico  flujo  
de  trabajo  de  ejecución  de  edición  y  compilación  de  muchos  otros  lenguajes  de  programación.  También  
proporciona  fácil  acceso  al  shell  y  al  sistema  de  archivos  de  su  sistema  operativo.  Dado  que  gran  parte  de  la  
codificación  del  análisis  de  datos  implica  exploración,  prueba  y  error  e  iteración,  IPython  puede  ayudarlo  a  realizar  
el  trabajo  más  rápido.

En  2014,  Fernando  y  el  equipo  de  IPython  anunciaron  el  proyecto  Jupyter,  una  iniciativa  más  amplia  para  diseñar  
herramientas  informáticas  interactivas  independientes  del  lenguaje.  El  cuaderno  web  IPython  se  convirtió  en  el  
cuaderno  Jupyter,  con  soporte  ahora  para  más  de  40  lenguajes  de  programación.  El  sistema  IPython  ahora  se  
puede  usar  como  kernel  (un  modo  de  lenguaje  de  programación)  para  usar  Python  con  Jupyter.

IPython  se  ha  convertido  en  un  componente  del  proyecto  de  código  abierto  mucho  más  amplio  Jupyter,  que  
proporciona  un  entorno  productivo  para  la  computación  interactiva  y  exploratoria.  Su  "modo"  más  antiguo  y  simple  
es  como  un  shell  de  Python  mejorado  diseñado  para  acelerar  la  escritura,  prueba  y  depuración  del  código  de  
Python.  También  puede  usar  el  sistema  IPython  a  través  de  Jupyter  Notebook,  un  “cuaderno  de  notas”  de  código  
interactivo  basado  en  la  web  que  ofrece  soporte  para  docenas  de  lenguajes  de  programación.  Los  cuadernos  
IPython  shell  y  Jupyter  son  especialmente  útiles  para  la  exploración  y  visualización  de  datos.

El  sistema  de  cuadernos  Jupyter  también  le  permite  crear  contenido  en  Markdown  y  HTML,  lo  que  le  brinda  un  
medio  para  crear  documentos  enriquecidos  con  código  y  texto.  Otros  lenguajes  de  programación  también  han  
implementado  kernels  para  Jupyter  para  permitirle  usar  lenguajes  distintos  a  Python  en  Jupyter.

Para  mí,  IPython  suele  estar  involucrado  con  la  mayoría  de  mi  trabajo  de  Python,  incluida  la  ejecución,  la  
depuración  y  la  prueba  del  código.

En  los  materiales  del  libro  adjunto,  encontrará  cuadernos  Jupyter  que  contienen  todos  los  ejemplos  de  código  de  
cada  capítulo.

SciPy  

SciPy  es  una  colección  de  paquetes  que  abordan  varios  dominios  de  problemas  estándar  diferentes  en  
computación  científica.  Aquí  hay  una  muestra  de  los  paquetes  incluidos:

6  |  Capítulo  1:  Preliminares
Machine Translated by Google

scipy.integrate  Rutinas  
de  integración  numérica  y  solucionadores  de  ecuaciones  diferenciales

scipy.linalg  Rutinas  
de  álgebra  lineal  y  descomposiciones  de  matrices  que  se  extienden  más  allá  de  las  proporcionadas  en  
numpy.linalg

scipy.optimize  
Optimizadores  de  funciones  (minimizadores)  y  algoritmos  de  búsqueda  de  raíces

scipy.signal  

Herramientas  de  procesamiento  de  señales

scipy.sparse  
Matrices  dispersas  y  solucionadores  de  sistemas  lineales  dispersos

scipy.special  
Wrapper  alrededor  de  SPECFUN,  una  biblioteca  de  Fortran  que  implementa  muchas  funciones  
matemáticas  comunes,  como  la  función  gamma

scipy.stats
Distribuciones  de  probabilidad  discretas  y  continuas  estándar  (funciones  de  densidad,  muestreadores,  
funciones  de  distribución  continua),  varias  pruebas  estadísticas  y  estadísticas  más  descriptivas

Juntos,  NumPy  y  SciPy  forman  una  base  computacional  razonablemente  completa  y  madura  para  muchas  
aplicaciones  informáticas  científicas  tradicionales.

scikit­aprender

Desde  el  inicio  del  proyecto  en  2010,  scikit­learn  se  ha  convertido  en  el  principal  conjunto  de  herramientas  
de  aprendizaje  automático  de  uso  general  para  programadores  de  Python.  En  solo  siete  años,  ha  tenido  más  
de  1.500  colaboradores  de  todo  el  mundo.  Incluye  submódulos  para  modelos  como:

•  Clasificación:  SVM,  vecinos  más  cercanos,  bosque  aleatorio,  regresión  logística,  etc.  •  Regresión:  

Lasso,  regresión  de  cresta,  etc.  •  Agrupamiento:  k­

medias,  agrupamiento  espectral,  etc.  •  Reducción  de  

dimensionalidad:  PCA,  selección  de  características,  factorización  de  matrices,  etc.  •  Selección  de  

modelos:  búsqueda  en  grillas,  validación  cruzada,  métricas

•  Preprocesamiento:  extracción  de  características,  normalización

Junto  con  pandas,  statsmodels  e  IPython,  scikit­learn  ha  sido  fundamental  para  permitir  que  Python  sea  un  
lenguaje  de  programación  de  ciencia  de  datos  productivo.  Mientras  no  lo  haré

1.3  Bibliotecas  esenciales  de  Python  |  7
Machine Translated by Google

poder  incluir  una  guía  completa  de  scikit­learn  en  este  libro,  daré  una  breve  introducción  a  algunos  
de  sus  modelos  y  cómo  usarlos  con  las  otras  herramientas  presentadas  en  el  libro.

modelos  estadisticos

statsmodels  es  un  paquete  de  análisis  estadístico  que  fue  iniciado  por  el  trabajo  del  profesor  de  
estadística  de  la  Universidad  de  Stanford,  Jonathan  Taylor,  quien  implementó  una  serie  de  modelos  
de  análisis  de  regresión  populares  en  el  lenguaje  de  programación  R.  Skipper  Seabold  y  Josef  
Perktold  crearon  formalmente  el  nuevo  proyecto  statsmodels  en  2010  y  desde  entonces  han  hecho  
crecer  el  proyecto  hasta  alcanzar  una  masa  crítica  de  usuarios  y  colaboradores  comprometidos.  
Nathaniel  Smith  desarrolló  el  proyecto  Patsy,  que  proporciona  un  marco  de  especificación  de  
fórmulas  o  modelos  para  modelos  estadísticos  inspirados  en  el  sistema  de  fórmulas  de  R.

En  comparación  con  scikit­learn,  statsmodels  contiene  algoritmos  para  estadísticas  y  econometría  
clásicas  (principalmente  frecuentistas).  Esto  incluye  submódulos  como:

•  Modelos  de  regresión:  Regresión  lineal,  modelos  lineales  generalizados,  modelos  lineales  
robustos,  modelos  lineales  de  efectos  mixtos,  etc.

•  Análisis  de  varianza  (ANOVA)  •  

Análisis  de  series  temporales:  AR,  ARMA,  ARIMA,  VAR  y  otros  modelos  •  

Métodos  no  paramétricos:  estimación  de  densidad  kernel,  regresión  kernel  •  
Visualización  de  resultados  de  modelos  estadísticos

statsmodels  se  centra  más  en  la  inferencia  estadística,  proporcionando  estimaciones  de  incertidumbre  
y  valores  p  para  los  parámetros.  scikit­learn,  por  el  contrario,  se  centra  más  en  la  predicción.

Al  igual  que  con  scikit­learn,  daré  una  breve  introducción  a  statsmodels  y  cómo  usarlo  con  NumPy  y  
pandas.

1.4  Instalación  y  configuración
Dado  que  todos  usan  Python  para  diferentes  aplicaciones,  no  existe  una  solución  única  para  
configurar  Python  y  los  paquetes  complementarios  necesarios.  Muchos  lectores  no  tendrán  un  
entorno  de  desarrollo  de  Python  completo  adecuado  para  seguir  este  libro,  por  lo  que  aquí  daré  
instrucciones  detalladas  para  configurar  cada  sistema  operativo.  Recomiendo  usar  la  distribución  
gratuita  de  Anaconda.  En  el  momento  de  escribir  este  artículo,  Anaconda  se  ofrece  en  las  formas  
Python  2.7  y  3.6,  aunque  esto  podría  cambiar  en  algún  momento  en  el  futuro.  Este  libro  usa  Python  
3.6,  y  lo  animo  a  usar  Python  3.6  o  superior.

8  |  Capítulo  1:  Preliminares
Machine Translated by Google

ventanas
Para  comenzar  en  Windows,  descargue  el  instalador  de  Anaconda.  Recomiendo  seguir  las  
instrucciones  de  instalación  para  Windows  disponibles  en  la  página  de  descarga  de  Anaconda,  que  
pueden  haber  cambiado  entre  el  momento  en  que  se  publicó  este  libro  y  el  momento  en  que  lo  está  
leyendo.

Ahora,  verifiquemos  que  las  cosas  estén  configuradas  correctamente.  Para  abrir  la  aplicación  Símbolo  
del  sistema  (también  conocida  como  cmd.exe),  haga  clic  con  el  botón  derecho  en  el  menú  Inicio  y  
seleccione  Símbolo  del  sistema.  Intente  iniciar  el  intérprete  de  Python  escribiendo  python.  Debería  
ver  un  mensaje  que  coincida  con  la  versión  de  Anaconda  que  instaló:

C:\Usuarios\wesm>python  
Python  3.5.2  |Anaconda  4.1.1  (64  bits)|  (predeterminado,  5  de  julio  de  2016,  11:41:13)
[MSC  v.1900  de  64  bits  (AMD64)]  en  win32
>>>

Para  salir  del  shell,  presione  Ctrl­D  (en  Linux  o  macOS),  Ctrl­Z  (en  Windows),  o  escriba  el  comando  
exit()  y  presione  Entrar.

Apple  (OS  X,  mac  OS)
Descargue  el  instalador  de  OS  X  Anaconda,  que  debería  tener  un  nombre  similar  a  Anaconda3­4.1.0­
MacOSX­x86_64.pkg.  Haga  doble  clic  en  el  archivo .pkg  para  ejecutar  el  instalador.
Cuando  se  ejecuta  el  instalador,  agrega  automáticamente  la  ruta  del  ejecutable  de  Anaconda  a  su  
archivo .bash_profile.  Este  se  encuentra  en /Users/$USER/.bash_profile.

Para  verificar  que  todo  funcione,  intente  iniciar  IPython  en  el  shell  del  sistema  (abra  la  aplicación  
Terminal  para  obtener  un  símbolo  del  sistema):

$  ipython

Para  salir  del  shell,  presione  Ctrl­D  o  escriba  exit()  y  presione  Enter.

GNU/Linux
Los  detalles  de  Linux  variarán  un  poco  dependiendo  de  su  versión  de  Linux,  pero  aquí  doy  detalles  
para  distribuciones  como  Debian,  Ubuntu,  CentOS  y  Fedora.  La  configuración  es  similar  a  OS  X,  con  
la  excepción  de  cómo  se  instala  Anaconda.  El  instalador  es  un  script  de  shell  que  debe  ejecutarse  en  
la  terminal.  Dependiendo  de  si  tiene  un  sistema  de  32  bits  o  de  64  bits,  deberá  instalar  el  instalador  
x86  (32  bits)  o  x86_64  (64  bits).  Luego  tendrá  un  archivo  con  un  nombre  similar  a  Anaconda3­4.1.0­
Linux­x86_64.sh.
Para  instalarlo,  ejecute  este  script  con  bash:

$  bash  Anaconda3­4.1.0­Linux­x86_64.sh

1.4  Instalación  y  configuración  |  9
Machine Translated by Google

Algunas  distribuciones  de  Linux  tienen  versiones  de  todos  los  paquetes  de  Python  
necesarios  en  sus  administradores  de  paquetes  y  se  pueden  instalar  con  una  
herramienta  como  apt.  La  configuración  que  se  describe  aquí  utiliza  Anaconda,  ya  
que  es  fácilmente  reproducible  en  todas  las  distribuciones  y  es  más  sencillo  
actualizar  los  paquetes  a  sus  últimas  versiones.

Después  de  aceptar  la  licencia,  se  le  presentará  la  opción  de  dónde  colocar  los  archivos  de  
Anaconda.  Recomiendo  instalar  los  archivos  en  la  ubicación  predeterminada  en  su  directorio  de  
inicio,  por  ejemplo, /home/$USUARIO/anaconda  (con  su  nombre  de  usuario,  naturalmente).

El  instalador  de  Anaconda  puede  preguntarle  si  desea  anteponer  su  directorio  bin/  a  su  variable  
$PATH .  Si  tiene  algún  problema  después  de  la  instalación,  puede  hacerlo  usted  mismo  
modificando  su .bashrc  (o .zshrc,  si  está  usando  el  shell  zsh)  con  algo  similar
a:

exportar  RUTA=/inicio/$USUARIO/anaconda/bin:$RUTA

Después  de  hacer  esto,  puede  iniciar  un  nuevo  proceso  de  terminal  o  ejecutar  su .bashrc  
nuevamente  con  source  ~/.bashrc.

Instalación  o  actualización  de  paquetes  de  Python  En  

algún  momento  durante  la  lectura,  es  posible  que  desee  instalar  paquetes  de  Python  adicionales  
que  no  están  incluidos  en  la  distribución  de  Anaconda.  En  general,  estos  se  pueden  instalar  con  
el  siguiente  comando:

conda  install  nombre_paquete

Si  esto  no  funciona,  también  puede  instalar  el  paquete  usando  la  herramienta  de  administración  
de  paquetes  pip:

pip  install  nombre_paquete

Puede  actualizar  paquetes  usando  el  comando  de  actualización  conda :

actualización  de  conda  nombre_del_paquete

pip  también  admite  actualizaciones  usando  el  indicador  ­­upgrade :

pip  install  ­­upgrade  nombre_paquete

Tendrá  varias  oportunidades  de  probar  estos  comandos  a  lo  largo  del  libro.

Si  bien  puede  usar  conda  y  pip  para  instalar  paquetes,  no  debe  intentar  actualizar  
los  paquetes  conda  con  pip,  ya  que  hacerlo  puede  generar  problemas  ambientales.  
Cuando  utilice  Anaconda  o  Miniconda,  es  mejor  intentar  primero  actualizar  con  
conda.

10  |  Capítulo  1:  Preliminares
Machine Translated by Google

Python  2  y  Python  3  La  
primera  versión  de  la  línea  de  intérpretes  Python  3.x  se  lanzó  a  finales  de  2008.
Incluyó  una  serie  de  cambios  que  hicieron  que  algunos  códigos  de  Python  2.x  previamente  escritos  fueran  
incompatibles.  Debido  a  que  habían  pasado  17  años  desde  el  primer  lanzamiento  de  Python  en  1991,  se  consideró  
que  la  creación  de  un  lanzamiento  "innovador"  de  Python  3  era  un  bien  mayor  dadas  las  lecciones  aprendidas  
durante  ese  tiempo.

En  2012,  gran  parte  de  la  comunidad  científica  y  de  análisis  de  datos  todavía  usaba  Python  2.x  porque  muchos  
paquetes  no  se  habían  hecho  completamente  compatibles  con  Python  3.  Por  lo  tanto,  la  primera  edición  de  este  libro  
utilizó  Python  2.7.  Ahora,  los  usuarios  son  libres  de  elegir  entre  Python  2.x  y  3.x  y,  en  general,  tienen  soporte  
completo  de  biblioteca  con  cualquiera  de  los  dos  tipos.

Sin  embargo,  Python  2.x  llegará  al  final  de  su  vida  útil  de  desarrollo  en  2020  (incluidos  los  parches  de  seguridad  
críticos),  por  lo  que  ya  no  es  una  buena  idea  comenzar  nuevos  proyectos  en  Python  2.7.  Por  lo  tanto,  este  libro  utiliza  
Python  3.6,  una  versión  estable  ampliamente  implementada  y  bien  respaldada.  Hemos  comenzado  a  llamar  a  Python  
2.x  "Legacy  Python"  y  a  Python  3.x  simplemente  "Python".  Yo  te  animo  a  que  hagas  lo  mismo.

Este  libro  utiliza  Python  3.6  como  base.  Su  versión  de  Python  puede  ser  más  reciente  que  la  3.6,  pero  los  ejemplos  
de  código  deben  ser  compatibles  con  versiones  anteriores.  Algunos  ejemplos  de  código  pueden  funcionar  de  manera  
diferente  o  no  funcionar  en  absoluto  en  Python  2.7.

Entornos  de  desarrollo  integrados  (IDE)  y  editores  de  texto  Cuando  me  preguntan  
sobre  mi  entorno  de  desarrollo  estándar,  casi  siempre  digo  "IPython  más  un  editor  de  
texto".  Por  lo  general,  escribo  un  programa  y  pruebo  y  depuro  iterativamente  cada  parte  
en  cuadernos  IPython  o  Jupyter.  También  es  útil  poder  jugar  con  los  datos  de  forma  
interactiva  y  verificar  visualmente  que  un  conjunto  particular  de  manipulaciones  de  datos  
está  haciendo  lo  correcto.  Bibliotecas  como  pandas  y  NumPy  están  diseñadas  para  ser  
fáciles  de  usar  en  el  shell.

Sin  embargo,  al  crear  software,  algunos  usuarios  pueden  preferir  usar  un  IDE  con  más  funciones  en  lugar  de  un  
editor  de  texto  comparativamente  primitivo  como  Emacs  o  Vim.  Aquí  hay  algunos  que  puedes  explorar:

•  PyDev  (gratis),  un  IDE  integrado  en  la  plataforma  Eclipse  •  PyCharm  

de  JetBrains  (basado  en  suscripción  para  usuarios  comerciales,  gratuito  para  desarrolladores  de  código  abierto)

•  Python  Tools  para  Visual  Studio  (para  usuarios  de  Windows)  •  Spyder  

(gratis),  un  IDE  que  actualmente  se  envía  con  Anaconda  •  Komodo  IDE  

(comercial)

1.4  Instalación  y  configuración  | 11
Machine Translated by Google

Debido  a  la  popularidad  de  Python,  la  mayoría  de  los  editores  de  texto,  como  Atom  y  Sublime  Text  2,  tienen  
una  excelente  compatibilidad  con  Python.

1.5  Comunidad  y  Conferencias
Fuera  de  una  búsqueda  en  Internet,  las  diversas  listas  de  correo  de  Python  científicas  y  relacionadas  con  
datos  son  generalmente  útiles  y  responden  a  las  preguntas.  Algunos  para  echar  un  vistazo  incluyen:

•  pydata:  una  lista  de  grupos  de  Google  para  preguntas  relacionadas  con  Python  para  el  análisis  de  datos  y
pandas

•  pystatsmodels:  para  preguntas  relacionadas  con  statsmodels  o  pandas  •  Lista  

de  correo  para  scikit­learn  (scikit­[email protected])  y  aprendizaje  automático  en  Python,  en  general

•  numpy­discussion:  para  preguntas  relacionadas  con  NumPy  •  

scipy­user:  para  preguntas  generales  sobre  SciPy  o  Python  científico

Deliberadamente  no  publiqué  URL  para  estos  en  caso  de  que  cambien.  Se  pueden  localizar  fácilmente  a  
través  de  una  búsqueda  en  Internet.

Cada  año  se  llevan  a  cabo  muchas  conferencias  en  todo  el  mundo  para  programadores  de  Python.  Si  desea  
conectarse  con  otros  programadores  de  Python  que  comparten  sus  intereses,  lo  animo  a  explorar  la  posibilidad  
de  asistir  a  uno,  si  es  posible.  Muchas  conferencias  tienen  apoyo  financiero  disponible  para  aquellos  que  no  
pueden  pagar  la  entrada  o  el  viaje  a  la  conferencia.  Aquí  hay  algunos  a  considerar:

•  PyCon  y  EuroPython:  Las  dos  principales  conferencias  generales  de  Python  en  North
América  y  Europa,  respectivamente

•  SciPy  y  EuroSciPy:  conferencias  orientadas  a  la  computación  científica  en  América  del  Norte
ica  y  Europa,  respectivamente

•  PyData:  una  serie  mundial  de  conferencias  regionales  dirigidas  a  la  ciencia  de  datos  y
casos  de  uso  de  análisis  de  datos

•  Conferencias  internacionales  y  regionales  de  PyCon  (ver  http://pycon.org  para  una  com
lista  completa)

1.6  Navegando  por  este  libro
Si  nunca  antes  ha  programado  en  Python,  querrá  pasar  algún  tiempo  en  los  Capítulos  2  y  3,  donde  he  
colocado  un  tutorial  condensado  sobre  las  características  del  lenguaje  Python  y  el  shell  IPython  y  los  
cuadernos  Jupyter.  Estas  cosas  son  un  requisito  previo

12  |  Capítulo  1:  Preliminares
Machine Translated by Google

conocimiento  para  el  resto  del  libro.  Si  ya  tiene  experiencia  con  Python,  puede  optar  por  hojear  u  omitir  
estos  capítulos.

A  continuación,  doy  una  breve  introducción  a  las  características  clave  de  NumPy,  dejando  el  uso  más  
avanzado  de  NumPy  para  el  Apéndice  A.  Luego,  presento  pandas  y  dedico  el  resto  del  libro  a  temas  de  
análisis  de  datos  aplicando  pandas,  NumPy  y  matplotlib  (para  visualización).  alización).  He  estructurado  
el  material  de  la  manera  más  incremental  posible,  aunque  ocasionalmente  hay  algunos  cruces  menores  
entre  capítulos,  con  algunos  casos  aislados  en  los  que  se  utilizan  conceptos  que  aún  no  se  han  
introducido  necesariamente.

Si  bien  los  lectores  pueden  tener  muchos  objetivos  finales  diferentes  para  su  trabajo,  las  tareas  
requeridas  generalmente  se  dividen  en  varios  grupos  amplios  diferentes:

Interactuando  con  el  mundo  exterior
Leer  y  escribir  con  una  variedad  de  formatos  de  archivo  y  almacenes  de  datos

Preparación
Limpiar,  mezclar,  combinar,  normalizar,  remodelar,  rebanar  y  trocear,  y  transformar  datos  para  su  
análisis

Transformación  
Aplicar  operaciones  matemáticas  y  estadísticas  a  grupos  de  conjuntos  de  datos  para  derivar  
nuevos  conjuntos  de  datos  (p.  ej.,  agregar  una  tabla  grande  por  variables  de  grupo)

Modelado  y  computación
Conexión  de  sus  datos  a  modelos  estadísticos,  algoritmos  de  aprendizaje  automático  u  otras  
herramientas  informáticas

Presentación
Crear  visualizaciones  gráficas  estáticas  o  interactivas  o  resúmenes  textuales

Ejemplos  de  código

La  mayoría  de  los  ejemplos  de  código  en  el  libro  se  muestran  con  entrada  y  salida  tal  y  como  aparecería  
ejecutado  en  el  shell  de  IPython  o  en  los  cuadernos  de  Jupyter:

En  [5]:  CÓDIGO  EJEMPLO
Salida[5]:  SALIDA

Cuando  vea  un  ejemplo  de  código  como  este,  la  intención  es  que  escriba  el  código  de  ejemplo  en  el  
bloque  de  entrada  en  su  entorno  de  codificación  y  lo  ejecute  presionando  la  tecla  Intro  (o  Shift­Enter  en  
Jupyter).  Debería  ver  un  resultado  similar  al  que  se  muestra  en  el  bloque  Salida .

Datos  para  ejemplos  Los  

conjuntos  de  datos  para  los  ejemplos  de  cada  capítulo  están  alojados  en  un  repositorio  de  GitHub.  
Puede  descargar  estos  datos  utilizando  el  sistema  de  control  de  versiones  de  Git  en  el  comando

1.6  Navegando  por  este  libro  |  13
Machine Translated by Google

línea  o  descargando  un  archivo  zip  del  repositorio  desde  el  sitio  web.  Si  tiene  problemas,  navegue  a  mi  
sitio  web  para  obtener  instrucciones  actualizadas  sobre  cómo  obtener  los  materiales  del  libro.

He  hecho  todo  lo  posible  para  asegurarme  de  que  contenga  todo  lo  necesario  para  reproducir  los  ejemplos,  
pero  es  posible  que  haya  cometido  algunos  errores  u  omisiones.  Si  es  así,  envíeme  un  correo  electrónico:  
[email protected].  La  mejor  manera  de  informar  errores  en  el  libro  es  en  la  página  de  erratas  en  
el  sitio  web  de  O'Reilly.

Convenciones  de  importación

La  comunidad  de  Python  ha  adoptado  una  serie  de  convenciones  de  nomenclatura  para  los  módulos  de  
uso  común:

importar  numpy  como  
np  importar  matplotlib.pyplot  como  plt  
importar  pandas  como  
pd  importar  seaborn  como  
sns  importar  statsmodels  como  sm

Esto  significa  que  cuando  ve  np.arange,  esta  es  una  referencia  a  la  función  arange  en  NumPy.  Esto  se  
hace  porque  se  considera  una  mala  práctica  en  el  desarrollo  de  software  de  Python  importar  todo  (desde  
numpy  import  *)  desde  un  paquete  grande  como  NumPy.

Jerga  

Usaré  algunos  términos  comunes  tanto  a  la  programación  como  a  la  ciencia  de  datos  con  los  que  quizás  
no  esté  familiarizado.  Por  lo  tanto,  aquí  hay  algunas  definiciones  breves:

Munge/munging/disputas
Describe  el  proceso  general  de  manipulación  de  datos  no  estructurados  y/o  desordenados  en  una  
forma  estructurada  o  limpia.  La  palabra  se  ha  colado  en  la  jerga  de  muchos  hackers  de  datos  
modernos.  “Munge”  rima  con  “grunge”.

pseudocódigo
Una  descripción  de  un  algoritmo  o  proceso  que  toma  una  forma  similar  a  un  código  aunque  
probablemente  no  sea  un  código  fuente  válido  real.

Azúcar  sintáctico  
Sintaxis  de  programación  que  no  agrega  nuevas  funciones,  pero  hace  que  algo  sea  más  conveniente  
o  más  fácil  de  escribir.

14  |  Capítulo  1:  Preliminares
Machine Translated by Google

CAPITULO  2

Conceptos  básicos  del  lenguaje  Python,  IPython  y
Cuadernos  Jupyter

Cuando  escribí  la  primera  edición  de  este  libro  en  2011  y  2012,  había  menos  recursos  disponibles  para  
aprender  a  hacer  análisis  de  datos  en  Python.  Esto  fue  en  parte  un  problema  del  huevo  y  la  gallina;  
muchas  bibliotecas  que  ahora  damos  por  sentadas,  como  pandas,  scikit­learn  y  statsmodels,  eran  
comparativamente  inmaduras  en  ese  entonces.  En  2017,  ahora  hay  una  creciente  literatura  sobre  ciencia  
de  datos,  análisis  de  datos  y  aprendizaje  automático,  que  complementa  los  trabajos  anteriores  sobre  
computación  científica  de  propósito  general  dirigidos  a  científicos  computacionales,  físicos  y  profesionales  
en  otros  campos  de  investigación.  También  hay  excelentes  libros  sobre  cómo  aprender  el  lenguaje  de  
programación  Python  y  convertirse  en  un  ingeniero  de  software  efectivo.

Como  este  libro  pretende  ser  un  texto  introductorio  para  trabajar  con  datos  en  Python,  creo  que  es  
valioso  tener  una  descripción  general  independiente  de  algunas  de  las  características  más  importantes  
de  las  estructuras  de  datos  y  bibliotecas  integradas  de  Python  desde  la  perspectiva  de  manipulación  de  
datos.  Por  lo  tanto,  en  este  capítulo  y  en  el  Capítulo  3  solo  presentaré  la  información  suficiente  para  
permitirle  continuar  con  el  resto  del  libro.

En  mi  opinión,  no  es  necesario  volverse  competente  en  la  creación  de  un  buen  software  en  Python  para  
poder  realizar  análisis  de  datos  de  manera  productiva.  Lo  animo  a  usar  el  shell  IPython  y  los  cuadernos  
Jupyter  para  experimentar  con  los  ejemplos  de  código  y  explorar  la  documentación  de  los  diversos  tipos,  
funciones  y  métodos.  Si  bien  hice  todo  lo  posible  para  presentar  el  material  del  libro  de  forma  incremental,  
es  posible  que  ocasionalmente  encuentre  cosas  que  aún  no  se  han  presentado  por  completo.

Gran  parte  de  este  libro  se  centra  en  el  análisis  basado  en  tablas  y  las  herramientas  de  preparación  de  
datos  para  trabajar  con  grandes  conjuntos  de  datos.  Para  usar  esas  herramientas,  a  menudo  primero  
debe  hacer  algo  de  munging  para  acorralar  los  datos  desordenados  en  una  forma  tabular  (o  estructurada)  
más  agradable.  Afortunadamente,  Python  es  un  lenguaje  ideal  para  darle  forma  rápidamente  a  sus  datos.  El

15
Machine Translated by Google

cuanto  mayor  sea  su  facilidad  con  el  lenguaje  Python,  más  fácil  le  resultará  preparar  nuevos  conjuntos  
de  datos  para  el  análisis.

Algunas  de  las  herramientas  de  este  libro  se  exploran  mejor  desde  una  sesión  en  vivo  de  IPython  o  
Jupyter.  Una  vez  que  aprendas  a  poner  en  marcha  IPython  y  Jupyter,  te  recomiendo  que  sigas  los  
ejemplos  para  que  puedas  experimentar  y  probar  cosas  diferentes.  Al  igual  que  con  cualquier  entorno  
similar  a  una  consola  controlado  por  teclado,  desarrollar  memoria  muscular  para  los  comandos  
comunes  también  es  parte  de  la  curva  de  aprendizaje.

Hay  conceptos  introductorios  de  Python  que  este  capítulo  no  cubre,  
como  clases  y  programación  orientada  a  objetos,  que  pueden  resultarle  
útiles  en  su  incursión  en  el  análisis  de  datos  en  Python.
Para  profundizar  su  conocimiento  del  lenguaje  Python,  le  recomiendo  
que  complemente  este  capítulo  con  el  tutorial  oficial  de  Python  y,  
potencialmente,  uno  de  los  muchos  libros  excelentes  sobre  
programación  Python  de  propósito  general.  Algunas  recomendaciones  
para  comenzar  incluyen:

•  Libro  de  cocina  de  Python,  tercera  edición,  de  David  Beazley  y  
Brian  K.  Jones  
(O'Reilly)  •  Python  fluido  de  Luciano  Ramalho  
(O'Reilly)  •  Python  eficaz  de  Brett  Slatkin  (Pearson)

2.1  El  intérprete  de  Python
Python  es  un  lenguaje  interpretado.  El  intérprete  de  Python  ejecuta  un  programa  ejecutando  una  
declaración  a  la  vez.  El  intérprete  de  Python  interactivo  estándar  se  puede  invocar  en  la  línea  de  
comando  con  el  comando  python :

$  pitón  
Pitón  3.6.0  |  empaquetado  por  conda­forge  |  (predeterminado,  13  de  enero  de  2017,  23:17:12)
[GCC  4.8.2  20140120  (Red  Hat  4.8.2­15)]  en  Linux  Escriba  
"ayuda",  "derechos  de  autor",  "créditos"  o  "licencia"  para  obtener  más  información.  >>>  un  =  5

>>>  imprime(a)  
5

El  >>>  que  ves  es  el  indicador  donde  escribirás  expresiones  de  código.  Para  salir  del  intérprete  de  
Python  y  volver  al  símbolo  del  sistema,  puede  escribir  exit()  o  presionar  Ctrl­D.

Ejecutar  programas  de  Python  es  tan  simple  como  llamar  a  Python  con  un  archivo .py  como  primer  
argumento.  Supongamos  que  hubiéramos  creado  hello_world.py  con  estos  contenidos:

imprimir('Hola  mundo')

16  |  Capítulo  2:  Conceptos  básicos  del  lenguaje  Python,  IPython  y  Jupyter  Notebooks
Machine Translated by Google

Puede  ejecutarlo  ejecutando  el  siguiente  comando  (el  archivo  hello_world.py  debe  estar  en  el  
directorio  de  su  terminal  de  trabajo  actual):

$  python  hola_mundo.py  Hola  
mundo

Mientras  que  algunos  programadores  de  Python  ejecutan  todo  su  código  de  Python  de  esta  
manera,  aquellos  que  realizan  análisis  de  datos  o  computación  científica  utilizan  IPython,  un  
intérprete  de  Python  mejorado,  o  cuadernos  de  Jupyter,  cuadernos  de  código  basados  en  la  web  
creados  originalmente  dentro  del  proyecto  IPython.  Ofrezco  una  introducción  al  uso  de  IPython  y  
Jupyter  en  este  capítulo  y  he  incluido  una  mirada  más  profunda  a  la  funcionalidad  de  IPython  en  el  Apéndice  A.
Cuando  usa  el  comando  %run ,  IPython  ejecuta  el  código  en  el  archivo  especificado  en  el  mismo  
proceso,  lo  que  le  permite  explorar  los  resultados  de  forma  interactiva  cuando  termina:

$  ipython  
Python  3.6.0  |  empaquetado  por  conda­forge  |  (predeterminado,  13  de  enero  de  2017,  23:17:12)
Escriba  "derechos  de  autor",  "créditos"  o  "licencia"  para  obtener  más  información.

IPython  5.1.0 :  un  Python  interactivo  mejorado.
? ­>  Introducción  y  descripción  general  de  las  características  de  IPython .
%quickref  ­>  Referencia  rápida.  ayuda  
­>  Sistema  de  ayuda  propio  de  Python .  ¿objeto?  ­>  
Detalles  sobre  'objeto',  use  'objeto?'  para  detalles  adicionales.

En  [1]:  %run  hello_world.py  Hola  
mundo

En  [2]:

El  indicador  predeterminado  de  IPython  adopta  el  estilo  numerado  In  [2]:  en  comparación  con  el  
indicador  >>>  estándar .

2.2  Conceptos  básicos  de  IPython

En  esta  sección,  lo  pondremos  en  marcha  con  el  shell  IPython  y  el  cuaderno  Jupyter,  y  le  
presentaremos  algunos  de  los  conceptos  esenciales.

Ejecución  de  IPython  Shell
Puede  iniciar  el  shell  de  IPython  en  la  línea  de  comando  al  igual  que  iniciar  el  regular
Intérprete  de  Python  excepto  con  el  comando  ipython :

$  ipython  
Python  3.6.0  |  empaquetado  por  conda­forge  |  (predeterminado,  13  de  enero  de  2017,  23:17:12)
Escriba  "derechos  de  autor",  "créditos"  o  "licencia"  para  obtener  más  información.

IPython  5.1.0:  un  Python  interactivo  mejorado. ?
­>  Introducción  y  descripción  general  de  las  características  de  IPython.  
%quickref  ­>  Referencia  rápida.  ayuda
­>  Sistema  de  ayuda  propio  de  Python.

2.2  Conceptos  básicos  de  IPython  |  17
Machine Translated by Google

¿objeto?  ­>  Detalles  sobre  'objeto',  use  'objeto?'  para  detalles  adicionales.

En  [1]:  a  =  5

En  [2]:  un
Salida[2]:  5

Puede  ejecutar  declaraciones  de  Python  arbitrarias  escribiéndolas  y  presionando  Retorno  (o  Intro).  Cuando  
escribe  solo  una  variable  en  IPython,  muestra  una  representación  de  cadena  del  objeto:

En  [5]:  importar  numpy  como  np

En  [6]:  datos  =  {i :  np.random.randn()  para  i  en  el  rango  (7)}

Entrada  [7]:  
salida  
de  datos  [7]:  {0:  
­0.20470765948471295,  1:  
0.47894333805754824,  2:  
­0.5194387150567381 ,  
3:  ­0.55573030434749,  4:  
1.9657805725027  142,  5:  
1,3934058329729904,  6:  0,09290787674371767}

Las  primeras  dos  líneas  son  declaraciones  de  código  de  Python;  la  segunda  declaración  crea  una  variable  con  
nombre  data  que  se  refiere  a  un  diccionario  de  Python  recién  creado.  La  última  línea  imprime  el  valor  de  los  
datos  en  la  consola.

Muchos  tipos  de  objetos  de  Python  están  formateados  para  que  sean  más  legibles,  o  bien  impresos,  lo  cual  es  
distinto  de  la  impresión  normal  con  print.  Si  imprimió  la  variable  de  datos  anterior  en  el  intérprete  estándar  de  
Python,  sería  mucho  menos  legible:

>>>  from  numpy.random  import  randn  >>>  
data  =  {i :  randn()  for  i  in  range(7)}  >>>  print(data)  
{0:  
­1.5948255432744511,  1:  0.10569006472787983,  2:  1.972367135977295,  3 :  
0,15455217573074576,  4:  ­0,24058577449429575,  5:  ­1,2904897053651216,  6:  
0,3308507317325902}

IPython  también  proporciona  funciones  para  ejecutar  bloques  arbitrarios  de  código  (a  través  de  un  enfoque  algo  
glorificado  de  copiar  y  pegar)  y  secuencias  de  comandos  completas  de  Python.  También  puede  usar  el  cuaderno  
Jupyter  Notebook  para  trabajar  con  bloques  de  código  más  grandes,  como  veremos  pronto.

Ejecución  de  Jupyter  Notebook  Uno  de  los  

principales  componentes  del  proyecto  Jupyter  es  el  notebook,  un  tipo  de  documento  interactivo  para  código,  
texto  (con  o  sin  marcado),  visualizaciones  de  datos  y  otros  resultados.  El  cuaderno  Jupyter  interactúa  con  los  
núcleos,  que  son  implementaciones  de

18  |  Capítulo  2:  Conceptos  básicos  del  lenguaje  Python,  IPython  y  Jupyter  Notebooks
Machine Translated by Google

el  protocolo  de  computación  interactiva  Jupyter  en  cualquier  número  de  lenguajes  de  programación.  El  
kernel  Jupyter  de  Python  utiliza  el  sistema  IPython  para  su  comportamiento  subyacente.

Para  iniciar  Jupyter,  ejecute  el  comando  jupyter  notebook  en  una  terminal:

$  jupyter  notebook  [I  
15:20:52.739  NotebookApp]  Sirviendo  portátiles  desde  el  directorio  local: /home/wesm/code/
pydata­book  [I  15:20:52.739  
NotebookApp]  0  núcleos  activos  [I  15:20:52.739  NotebookApp]  
El  Jupyter  Notebook  se  ejecuta  en:  http://localhost:8888/  [I  15:20:52.740  NotebookApp]  
Use  Control­C  para  detener  
este  servidor  y  cerrar  todos  los  núcleos  (dos  veces  para  omitir  la  confirmación).

Se  creó  una  nueva  ventana  en  la  sesión  del  navegador  existente.

En  muchas  plataformas,  Jupyter  se  abrirá  automáticamente  en  su  navegador  web  predeterminado  (a  menos  
que  lo  inicie  con  ­­no­browser).  De  lo  contrario,  puede  navegar  a  la  dirección  HTTP  impresa  cuando  inició  el  
portátil,  aquí  http://localhost:8888/.  Consulte  la  Figura  2­1  para  ver  cómo  se  ve  esto  en  Google  Chrome.

Mucha  gente  usa  Jupyter  como  un  entorno  informático  local,  pero  
también  se  puede  implementar  en  servidores  y  acceder  de  forma  
remota.  No  cubriré  esos  detalles  aquí,  pero  lo  animo  a  explorar  este  
tema  en  Internet  si  es  relevante  para  sus  necesidades.

Figura  2­1.  Página  de  destino  del  cuaderno  Jupyter

2.2  Conceptos  básicos  de  IPython  |  19
Machine Translated by Google

Para  crear  un  nuevo  cuaderno,  haga  clic  en  el  botón  Nuevo  y  seleccione  la  opción  "Python  3"  o  "conda  
[predeterminado]".  Debería  ver  algo  como  la  Figura  2­2.  Si  es  la  primera  vez,  intente  hacer  clic  en  la  "celda"  
de  código  vacía  e  ingrese  una  línea  de  código  Python.  Luego  presione  Shift­Enter  para  ejecutarlo.

Figura  2­2.  Nueva  vista  de  notebook  de  Jupyter

Cuando  guarda  el  cuaderno  (consulte  "Guardar  y  punto  de  control"  en  el  menú  Archivo  del  cuaderno),  se  
crea  un  archivo  con  la  extensión .ipynb.  Este  es  un  formato  de  archivo  independiente  que  contiene  todo  el  
contenido  (incluido  cualquier  resultado  de  código  evaluado)  actualmente  en  el  cuaderno.  Estos  pueden  ser  
cargados  y  editados  por  otros  usuarios  de  Jupyter.  Para  cargar  una  libreta  existente,  coloque  el  archivo  en  el  
mismo  directorio  donde  inició  el  proceso  de  la  libreta  (o  en  una  subcarpeta  dentro  de  este),  luego  haga  doble  
clic  en  el  nombre  de  la  página  de  destino.  Puede  probarlo  con  los  cuadernos  de  mi  repositorio  wesm/pydata­
book  en  GitHub.
Consulte  la  Figura  2­3.

Si  bien  el  cuaderno  de  Jupyter  puede  sentirse  como  una  experiencia  distinta  del  shell  de  IPython,  casi  todos  
los  comandos  y  herramientas  de  este  capítulo  se  pueden  usar  en  cualquier  entorno.
mento

20  |  Capítulo  2:  Conceptos  básicos  del  lenguaje  Python,  IPython  y  Jupyter  Notebooks
Machine Translated by Google

Figura  2­3.  Vista  de  ejemplo  de  Jupyter  para  un  cuaderno  existente

Finalización  de  

pestañas  En  la  superficie,  el  shell  de  IPython  parece  una  versión  cosméticamente  diferente  del  
intérprete  estándar  de  terminal  de  Python  (invocado  con  python).  Una  de  las  principales  mejoras  con  
respecto  al  shell  estándar  de  Python  es  la  finalización  con  pestañas,  que  se  encuentra  en  muchos  IDE  
u  otros  entornos  de  análisis  informático  interactivo.  Mientras  ingresa  expresiones  en  el  shell,  al  
presionar  la  tecla  Tab  buscará  en  el  espacio  de  nombres  cualquier  variable  (objetos,  funciones,  etc.)  
que  coincida  con  los  caracteres  que  ha  escrito  hasta  ahora:

En  [1]:  una_manzana  =  27

En  [2]:  un_ejemplo  =  42

En  [3]:  an<Tab>  
an_apple  y un_ejemplo  cualquiera

En  este  ejemplo,  tenga  en  cuenta  que  IPython  mostró  las  dos  variables  que  definí,  así  como  la  palabra  
clave  de  Python  y  la  función  incorporada  any.  Naturalmente,  también  puede  completar  métodos  y  
atributos  en  cualquier  objeto  después  de  escribir  un  punto:

2.2  Conceptos  básicos  de  IPython  |  21
Machine Translated by Google

En  [3]:  b  =  [1,  2,  3]

En  [4]:  b.<Tab>  
b.agregar  b.contar  b.insertar  b.revertir  b.borrar  
b.extender  b.pop  b.copiar   b.clasificar

b.indexar  b.eliminar

Lo  mismo  ocurre  con  los  módulos:

En  [1]:  importar  fecha  y  hora

En  [2]:  fechahora.<Tab>  
fechahora.fecha fechahora.MAXYEAR fechahora.timedelta
datetime.datetime  datetime.MINYEAR   datetime.timezone  
datetime.datetime_CAPI  datetime.time datetime.tzinfo

En  el  cuaderno  Jupyter  y  en  las  versiones  más  recientes  de  IPython  (5.0  y  posteriores),  las  funciones  de  
finalización  automática  se  muestran  en  un  cuadro  desplegable  en  lugar  de  como  salida  de  texto.

Tenga  en  cuenta  que  IPython  oculta  de  forma  predeterminada  los  métodos  y  
atributos  que  comienzan  con  guiones  bajos,  como  los  métodos  mágicos  y  los  
métodos  y  atributos  internos  "privados",  para  evitar  saturar  la  pantalla  (¡y  
confundir  a  los  usuarios  novatos!).  Estos  también  se  pueden  completar  con  
tabuladores,  pero  primero  debe  escribir  un  guión  bajo  para  verlos.  Si  prefiere  
ver  siempre  dichos  métodos  en  la  finalización  de  pestañas,  puede  cambiar  
esta  configuración  en  la  configuración  de  IPython.  Consulte  la  documentación  
de  IPython  para  averiguar  cómo  hacerlo.

La  finalización  de  pestañas  funciona  en  muchos  contextos  fuera  de  la  búsqueda  en  el  espacio  de  nombres  
interactivo  y  la  finalización  de  atributos  de  objetos  o  módulos.  Al  escribir  cualquier  cosa  que  parezca  una  ruta  
de  archivo  (incluso  en  una  cadena  de  Python),  presionar  la  tecla  Tab  completará  cualquier  cosa  en  el  sistema  
de  archivos  de  su  computadora  que  coincida  con  lo  que  ha  escrito:

En  [7]:  datasets/movielens/<Tab>  datasets/
movielens/movies.dat  datasets/movielens/README  datasets/movielens/
ratings.dat  datasets/movielens/users.dat

En  [7]:  ruta  =  'datasets/movielens/<Tab>  datasets/
movielens/movies.dat  datasets/movielens/README  datasets/movielens/
ratings.dat  datasets/movielens/users.dat

En  combinación  con  el  comando  %run  (consulte  “El  comando  %run”  en  la  página  25),  esta  funcionalidad  puede  
ahorrarle  muchas  pulsaciones  de  teclas.

Otra  área  en  la  que  la  finalización  con  tabulaciones  ahorra  tiempo  es  la  finalización  de  los  argumentos  de  
palabras  clave  de  función  (¡e  incluir  el  signo  = !).  Consulte  la  Figura  2­4.

22  |  Capítulo  2:  Conceptos  básicos  del  lenguaje  Python,  IPython  y  Jupyter  Notebooks
Machine Translated by Google

Figura  2­4.  Palabras  clave  de  función  de  autocompletar  en  el  cuaderno  Jupyter

Echaremos  un  vistazo  más  de  cerca  a  las  funciones  en  un  momento.

Introspección  El  uso  

de  un  signo  de  interrogación  (?)  antes  o  después  de  una  variable  mostrará  información  general  sobre  el  objeto:

En  [8]:  b  =  [1,  2,  3]

En  [9]:  b?
Tipo:  lista  Cadena  
Forma:[1,  2,  3]
Longitud:   3
Docstring:  
list  ()  ­>  nueva  lista  de  lista  vacía  
(iterable)  ­>  nueva  lista  inicializada  a  partir  de  elementos  iterables

En  [10]:  imprimir?
Cadena  de  
documentación:  imprimir(valor, ...,  sep='  ',  end='\n',  file=sys.stdout,  flush=False)

Imprime  los  valores  en  una  secuencia  o  en  sys.stdout  de  forma  predeterminada.
Argumentos  de  palabras  clave  
opcionales:  archivo:  un  objeto  similar  a  un  archivo  (flujo);  el  valor  predeterminado  es  el  sys.stdout  
actual .  sep:  cadena  insertada  entre  valores,  por  defecto  un  espacio.  final:  
cadena  añadida  después  del  último  valor,  por  defecto  una  nueva  línea.  flush:  si  se  
debe  enjuagar  a  la  fuerza  la  corriente.  
builtin_function_or_method  Tipo:

Esto  se  conoce  como  introspección  de  objetos.  Si  el  objeto  es  una  función  o  un  método  de  instancia,  también  se  mostrará  
la  cadena  de  documentación,  si  está  definida.  Supongamos  que  escribimos  la  siguiente  función  (que  puedes  reproducir  
en  IPython  o  Jupyter):

2.2  Conceptos  básicos  de  IPython  |  23
Machine Translated by Google

def  sumar_números(a,  b):
"""

Sumar  dos  números  juntos

Devoluciones
­­­­­­­
the_sum :  tipo  de  argumentos
"""
devolver  a  +  b

Entonces  usando ?  nos  muestra  el  docstring:

En  [11]:  agregar_números?
Firma:  add_numbers(a,  b)
Docstring:  
suma  dos  números  juntos

Devoluciones
­­­­­­­
the_sum :  tipo  de  argumentos
Archivo:  <ipython­input­9­6a548a216e27>
Tipo:  función

usando ??  también  mostrará  el  código  fuente  de  la  función  si  es  posible:

En  [12]:  agregar_números??
Firma:  add_numbers(a,  b)
Fuente:  
def  add_numbers(a,  b):
"""

Sumar  dos  números  juntos

Devoluciones
­­­­­­­
the_sum :  tipo  de  argumentos
"""
devolver  a  +  b
Archivo: <ipython­input­9­6a548a216e27>  función
Tipo:

?  tiene  un  uso  final,  que  es  para  buscar  en  el  espacio  de  nombres  de  IPython  de  manera  similar  a  
la  línea  de  comandos  estándar  de  Unix  o  Windows.  Varios  caracteres  combinados  con  el  comodín  
(*)  mostrarán  todos  los  nombres  que  coincidan  con  la  expresión  del  comodín.  Por  ejemplo,  
podríamos  obtener  una  lista  de  todas  las  funciones  en  el  espacio  de  nombres  NumPy  de  nivel  
superior  que  contenga  carga:

En  [13]:  np.*cargar*?  
np.__loader__  
np.load  
np.loads  
np.loadtxt  
np.pkgload

24  |  Capítulo  2:  Conceptos  básicos  del  lenguaje  Python,  IPython  y  Jupyter  Notebooks
Machine Translated by Google

El  comando  %ejecutar
Puede  ejecutar  cualquier  archivo  como  un  programa  de  Python  dentro  del  entorno  de  su  sesión  de  IPython  usando  el  

comando  %run .  Suponga  que  tiene  el  siguiente  script  simple  almacenado  en  ipython_script_test.py:

def  f(x,  y,  z):  return  (x  
+  y) /  z

a  =  5  b  
=  6
c  =  7,5

resultado  =  f(a,  b,  c)

Puede  ejecutar  esto  pasando  el  nombre  de  archivo  a  %run:

En  [14]:  %ejecutar  ipython_script_test.py

La  secuencia  de  comandos  se  ejecuta  en  un  espacio  de  nombres  vacío  (sin  importaciones  u  otras  variables  definidas),  por  
lo  que  el  comportamiento  debería  ser  idéntico  a  ejecutar  el  programa  en  la  línea  de  comandos  mediante  python  script.py.  

Todas  las  variables  (importaciones,  funciones  y  globales)  definidas  en  el  archivo  (hasta  que  se  genere  una  excepción,  si  la  
hay)  serán  accesibles  en  el  shell  de  IPython:

Entrada  [15]:  c  
Salida  [15]:  7,5

Entrada  [16]:  
resultado  Salida[16]:  1.4666666666666666

Si  una  secuencia  de  comandos  de  Python  espera  argumentos  de  la  línea  de  comandos  (que  se  encuentran  en  sys.argv),  
estos  se  pueden  pasar  después  de  la  ruta  del  archivo  como  si  se  ejecutaran  en  la  línea  de  comandos.

Si  desea  dar  acceso  a  un  script  a  las  variables  ya  definidas  en  el  
espacio  de  nombres  interactivo  de  IPython,  use  %run  ­i  en  lugar  de  
%run  simple .

En  el  cuaderno  de  Jupyter,  también  puede  usar  la  función  mágica  %load  relacionada,  que  importa  un  script  en  una  celda  de  
código:

>>>  %cargar  ipython_script_test.py

def  f(x,  y,  z):  return  (x  
+  y) /  z

a  =  5  b  
=  6
c  =  7,5

2.2  Conceptos  básicos  de  IPython  |  25
Machine Translated by Google

resultado  =  f(a,  b,  c)

Interrumpir  el  código  en  

ejecución  Al  presionar  Ctrl­C  mientras  se  ejecuta  cualquier  código,  ya  sea  un  script  a  través  de  %run  
o  un  comando  de  ejecución  prolongada,  se  generará  una  interrupción  del  teclado .  Esto  hará  que  
casi  todos  los  programas  de  Python  se  detengan  de  inmediato,  excepto  en  ciertos  casos  inusuales.

Cuando  una  parte  del  código  de  Python  ha  llamado  a  algunos  módulos  
de  extensión  compilados,  presionar  Ctrl­C  no  siempre  hará  que  la  
ejecución  del  programa  se  detenga  de  inmediato.  En  tales  casos,  tendrá  
que  esperar  hasta  que  se  devuelva  el  control  al  intérprete  de  Python  o,  
en  circunstancias  más  graves,  finalizar  por  la  fuerza  el  proceso  de  Python.

Ejecución  de  código  desde  el  portapapeles  Si  

está  utilizando  el  cuaderno  Jupyter,  puede  copiar  y  pegar  código  en  cualquier  celda  de  código  y  
ejecutarlo.  También  es  posible  ejecutar  código  desde  el  portapapeles  en  el  shell  de  IPython.
Suponga  que  tiene  el  siguiente  código  en  alguna  otra  aplicación:

x  =  5
y  =  7  
si  x  >  5:
X  +  =  1

y  =  8

Los  métodos  más  infalibles  son  las  funciones  mágicas  %paste  y  %cpaste .  %paste  toma  cualquier  
texto  que  esté  en  el  portapapeles  y  lo  ejecuta  como  un  solo  bloque  en  el  shell:

En  [17]:  %pegar  x  
=  5  y  
=  7  si  
x  >  5:
X  +  =  1

y  =  8
##  ­­  Fin  del  texto  pegado  ­­

%cpaste  es  similar,  excepto  que  le  brinda  un  aviso  especial  para  pegar  el  código  en:

En  [18]:  %cpaste  
Pegar  código;  ingrese  '­­'  solo  en  la  línea  para  detener  o  use  Ctrl­D. :x  =  5 :y  =  7 :si  x  >  
5:

: X  +  =  1
:

26  |  Capítulo  2:  Conceptos  básicos  del  lenguaje  Python,  IPython  y  Jupyter  Notebooks
Machine Translated by Google

: y  =  8
:­­

Con  el  bloque  %cpaste ,  tienes  la  libertad  de  pegar  tanto  código  como  quieras
antes  de  ejecutarlo.  Puede  decidir  usar  %cpaste  para  mirar  el  pegado
código  antes  de  ejecutarlo.  Si  pega  accidentalmente  el  código  incorrecto,  puede  romper
del  indicador  %cpaste  presionando  Ctrl­C.

Atajos  de  teclado  de  terminal
IPython  tiene  muchos  atajos  de  teclado  para  navegar  por  el  aviso  (que  le  resultará  familiar).
iar  a  los  usuarios  del  editor  de  texto  Emacs  o  el  shell  bash  de  Unix)  e  interactuar  con  el
historial  de  comandos  de  shell.  La  Tabla  2­1  resume  algunos  de  los  más  utilizados
atajos  Consulte  la  Figura  2­5  para  ver  una  ilustración  de  algunos  de  estos,  como  el  cursor
movimienot.

Figura  2­5.  Ilustración  de  algunos  métodos  abreviados  de  teclado  en  el  shell  de  IPython

Tabla  2­1.  Métodos  abreviados  de  teclado  estándar  de  IPython

Atajo  de  teclado Descripción

Ctrl­P  o  flecha  arriba Buscar  hacia  atrás  en  el  historial  de  comandos  los  comandos  que  comiencen  con  el  texto  ingresado  actualmente

Ctrl­N  o  flecha  hacia  abajo  Buscar  hacia  adelante  en  el  historial  de  comandos  los  comandos  que  comienzan  con  el  texto  ingresado  actualmente

Ctrl­R Búsqueda  de  historial  inverso  estilo  Readline  (coincidencia  parcial)

Ctrl­Mayús­V Pegar  texto  desde  el  portapapeles

Ctrl­C Interrumpir  el  código  que  se  está  ejecutando  actualmente

Ctrl­A Mover  el  cursor  al  principio  de  la  línea

Ctrl­E Mover  el  cursor  al  final  de  la  línea

Ctrl­K Eliminar  texto  del  cursor  hasta  el  final  de  la  línea

Ctrl­U Descartar  todo  el  texto  en  la  línea  actual

Ctrl­F Mover  el  cursor  hacia  adelante  un  carácter

Ctrl­B Mover  el  cursor  hacia  atrás  un  carácter

Ctrl­L Pantalla  clara

Tenga  en  cuenta  que  los  portátiles  Jupyter  tienen  un  conjunto  de  métodos  abreviados  de  teclado  en  gran  medida  independiente  para  la  navegación.

igación  y  edición.  Dado  que  estos  accesos  directos  han  evolucionado  más  rápidamente  que  los  de  IPython,
lo  animamos  a  explorar  el  sistema  de  ayuda  integrado  en  los  menús  de  Jupyter  Notebook.

2.2  Conceptos  básicos  de  IPython  |  27
Machine Translated by Google

Acerca  de  los  comandos  mágicos  

Los  comandos  especiales  de  IPython  (que  no  están  integrados  en  Python)  se  conocen  como  
comandos  "mágicos".  Estos  están  diseñados  para  facilitar  tareas  comunes  y  permitirle  controlar  
fácilmente  el  comportamiento  del  sistema  IPython.  Un  comando  mágico  es  cualquier  comando  
precedido  por  el  símbolo  de  porcentaje  %.  Por  ejemplo,  puede  verificar  el  tiempo  de  ejecución  de  
cualquier  declaración  de  Python,  como  una  multiplicación  de  matrices,  utilizando  la  función  mágica  
%timeit  (que  se  analizará  con  más  detalle  más  adelante):

En  [20]:  a  =  np.random.randn(100,  100)

En  [20]:  %timeit  np.dot(a,  a)  10000  
bucles,  lo  mejor  de  3:  20,9  µs  por  bucle

Los  comandos  mágicos  se  pueden  ver  como  programas  de  línea  de  comandos  que  se  ejecutan  
dentro  del  sistema  IPython.  Muchos  de  ellos  tienen  opciones  adicionales  de  "línea  de  comandos",  
que  se  pueden  ver  (como  era  de  esperar)  usando ?:

En  [21]:  %debug?
cadena  de  documentación:

::

%debug  [­­breakpoint  ARCHIVO:LINE]  [declaración  [declaración ...]]

Active  el  depurador  interactivo.

Este  comando  mágico  admite  dos  formas  de  activar  el  depurador.
Una  es  activar  el  depurador  antes  de  ejecutar  el  código.  De  esta  manera,  puede  establecer  
un  punto  de  interrupción  para  recorrer  el  código  desde  el  punto.
Puede  usar  este  modo  dando  declaraciones  para  ejecutar  y,  opcionalmente,  un  punto  de  
interrupción.

El  otro  es  activar  el  depurador  en  modo  post­mortem.  Puede  activar  este  modo  simplemente  
ejecutando  %debug  sin  ningún  argumento.
Si  acaba  de  ocurrir  una  excepción,  esto  le  permite  inspeccionar  sus  marcos  de  pila  de  forma  
interactiva.  Tenga  en  cuenta  que  esto  siempre  funcionará  solo  en  el  último  rastreo  que  ocurrió,  por  
lo  que  debe  llamarlo  rápidamente  después  de  que  se  haya  activado  una  excepción  que  
desea  inspeccionar,  porque  si  ocurre  otra,  golpea  a  la  anterior.

Si  desea  que  IPython  haga  esto  automáticamente  en  cada  excepción,  consulte  la  magia  de  
%pdb  para  obtener  más  detalles.

argumentos  posicionales:
declaración Código  para  ejecutar  en  el  depurador.  Puede  omitir  esto  en  el  modo  
de  magia  celular.

argumentos  opcionales:
­­breakpoint  <ARCHIVO:LINEA>,  ­b  <ARCHIVO:LINEA>  Establece  
el  punto  de  interrupción  en  la  LÍNEA  en  el  ARCHIVO.

28  |  Capítulo  2:  Conceptos  básicos  del  lenguaje  Python,  IPython  y  Jupyter  Notebooks
Machine Translated by Google

Las  funciones  mágicas  se  pueden  usar  de  forma  predeterminada  sin  el  signo  de  porcentaje,  siempre  que  no  haya  variaciones.
able  se  define  con  el  mismo  nombre  que  la  función  mágica  en  cuestión.  Esta  característica  es
llamado  automagic  y  se  puede  habilitar  o  deshabilitar  con  %automagic.

Algunas  funciones  mágicas  se  comportan  como  funciones  de  Python  y  su  salida  se  puede  asignar
a  una  variable:

En  [22]:  %contraseña
Salida[22]:  '/home/wesm/code/pydata­book

En  [23]:  foo  =  %pwd

En  [24]:  Foo
Salida[24]:  '/home/wesm/code/pydata­book'

Dado  que  se  puede  acceder  a  la  documentación  de  IPython  desde  el  sistema,  le  animo
para  explorar  todos  los  comandos  especiales  disponibles  escribiendo  %quickref  o  %magic.
La  Tabla  2­2  destaca  algunos  de  los  más  críticos  para  ser  productivo  en  interactivo
informática  y  desarrollo  de  Python  en  IPython.

Tabla  2­2.  Algunos  comandos  mágicos  de  IPython  de  uso  frecuente
Dominio Descripción

%refrápida Mostrar  la  tarjeta  de  referencia  rápida  de  IPython

%magia   Mostrar  documentación  detallada  de  todos  los  comandos  mágicos  disponibles

%depuración Ingrese  el  depurador  interactivo  en  la  parte  inferior  del  último  rastreo  de  excepción

%hist Imprima  el  historial  de  entrada  de  comandos  (y  salida  opcional)

%pdb Ingrese  automáticamente  al  depurador  después  de  cualquier  excepción

%pegar Ejecutar  código  Python  preformateado  desde  el  portapapeles

%cpegar Abra  un  aviso  especial  para  pegar  manualmente  el  código  de  Python  para  ejecutarlo

%reiniciar Eliminar  todas  las  variables/nombres  definidos  en  el  espacio  de  nombres  interactivo

%pagina  OBJETO Pretty­print  el  objeto  y  mostrarlo  a  través  de  un  buscapersonas

%ejecutar  secuencia  de  comandos.py Ejecute  un  script  de  Python  dentro  de  IPython

declaración  %prun Ejecutar  declaración  con  cProfile  e  informar  la  salida  del  generador  de  perfiles

%  declaración  de  tiempo Informar  el  tiempo  de  ejecución  de  una  sola  instrucción

declaración  %timeit Ejecute  una  declaración  varias  veces  para  calcular  un  tiempo  de  ejecución  promedio  conjunto;  útil  para

código  de  tiempo  con  tiempo  de  ejecución  muy  corto

%who,  %who_ls,  %whos  Mostrar  variables  definidas  en  un  espacio  de  nombres  interactivo,  con  distintos  niveles  de  información/
verbosidad

variable  %xdel Elimine  una  variable  e  intente  borrar  cualquier  referencia  al  objeto  en  las  partes  internas  de  IPython

Integración  con  matplotlib
Una  de  las  razones  de  la  popularidad  de  IPython  en  la  computación  analítica  es  que  se  integra  bien
con  visualización  de  datos  y  otras  bibliotecas  de  interfaz  de  usuario  como  matplotlib.  No  te  preocupes
si  nunca  ha  usado  matplotlib  antes;  se  discutirá  con  más  detalle  más  adelante  en

2.2  Conceptos  básicos  de  IPython  |  29
Machine Translated by Google

este  libro.  La  función  mágica  %matplotlib  configura  su  integración  con  el  shell  IPython  o  el  cuaderno  
Jupyter.  Esto  es  importante,  ya  que,  de  lo  contrario,  los  gráficos  que  cree  no  aparecerán  (notebook)  o  
tomarán  el  control  de  la  sesión  hasta  que  se  cierren  (shell).

En  el  shell  de  IPython,  ejecutar  %matplotlib  configura  la  integración  para  que  pueda  crear  varias  ventanas  
de  gráficos  sin  interferir  con  la  sesión  de  la  consola:

En  [26]:  %matplotlib
Usando  el  backend  de  matplotlib:  Qt4Agg

En  Jupyter,  el  comando  es  un  poco  diferente  (Figura  2­6):

En  [26]:  %matplotlib  en  línea

Figura  2­6.  Trazado  de  matplotlib  en  línea  de  Jupyter

2.3  Conceptos  básicos  del  lenguaje  Python

En  esta  sección,  le  daré  una  descripción  general  de  los  conceptos  esenciales  de  programación  de  Python  
y  la  mecánica  del  lenguaje.  En  el  próximo  capítulo,  entraré  en  más  detalles  sobre  las  estructuras  de  
datos,  funciones  y  otras  herramientas  integradas  de  Python.

Semántica  del  lenguaje  El  

diseño  del  lenguaje  Python  se  distingue  por  su  énfasis  en  la  legibilidad,  la  simplicidad  y  la  claridad.  
Algunas  personas  llegan  a  compararlo  con  un  "pseudocódigo  ejecutable".

Sangría,  no  llaves
Python  usa  espacios  en  blanco  (tabulaciones  o  espacios)  para  estructurar  el  código  en  lugar  de  usar  
llaves  como  en  muchos  otros  lenguajes  como  R,  C++,  Java  y  Perl.  Considere  un  bucle  for  de  un  algoritmo  
de  clasificación:

30  |  Capítulo  2:  Conceptos  básicos  del  lenguaje  Python,  IPython  y  Jupyter  Notebooks
Machine Translated by Google

para  x  en  matriz:  
si  x  <  pivote:  
menos.agregar(x)  
si  no:  
mayor.agregar(x)
Los  dos  puntos  indican  el  comienzo  de  un  bloque  de  código  con  sangría,  después  de  lo  cual  todo  el  código  

debe  tener  la  misma  sangría  hasta  el  final  del  bloque.

Lo  ames  o  lo  odies,  los  espacios  en  blanco  significativos  son  una  realidad  para  los  programadores  de  
Python  y,  según  mi  experiencia,  pueden  hacer  que  el  código  de  Python  sea  más  legible  que  otros  lenguajes  
que  he  usado.  Si  bien  puede  parecer  extraño  al  principio,  es  de  esperar  que  se  acostumbre  con  el  tiempo.

Recomiendo  encarecidamente  usar  cuatro  espacios  como  sangría  predeterminada  y  
reemplazar  las  pestañas  con  cuatro  espacios.  Muchos  editores  de  texto  tienen  una  
configuración  que  reemplazará  las  tabulaciones  con  espacios  automáticamente  (¡haz  esto!).
Algunas  personas  usan  tabulaciones  o  un  número  diferente  de  espacios,  y  dos  
espacios  no  son  terriblemente  raros.  En  general,  cuatro  espacios  es  el  estándar  
adoptado  por  la  gran  mayoría  de  los  programadores  de  Python,  por  lo  que  recomiendo  
hacerlo  en  ausencia  de  una  razón  convincente  de  lo  contrario.

Como  puede  ver  ahora,  las  declaraciones  de  Python  tampoco  necesitan  terminar  con  punto  y  coma.  Sin  
embargo,  se  pueden  usar  punto  y  coma  para  separar  varias  declaraciones  en  una  sola  línea:

a  =  5;  b  =  6;  c  =  7

Por  lo  general,  se  desaconseja  poner  varias  declaraciones  en  una  línea  en  Python,  ya  que  a  menudo  hace  
que  el  código  sea  menos  legible.

Todo  es  un  objeto  Una  

característica  importante  del  lenguaje  Python  es  la  consistencia  de  su  modelo  de  objetos.  Cada  número,  
cadena,  estructura  de  datos,  función,  clase,  módulo,  etc.  existe  en  el  intérprete  de  Python  en  su  propia  
"caja",  que  se  denomina  objeto  de  Python.
Cada  objeto  tiene  un  tipo  asociado  (por  ejemplo,  cadena  o  función)  y  datos  internos.  En  la  práctica,  esto  
hace  que  el  lenguaje  sea  muy  flexible,  ya  que  incluso  las  funciones  pueden  tratarse  como  cualquier  otro  
objeto.

Comentarios

El  intérprete  de  Python  ignora  cualquier  texto  precedido  por  la  almohadilla  (signo  de  almohadilla)  # .  Esto  se  
usa  a  menudo  para  agregar  comentarios  al  código.  A  veces  también  puede  querer  excluir  ciertos  bloques  
de  código  sin  eliminarlos.  Una  solución  fácil  es  comentar  el  código:

2.3  Conceptos  básicos  del  lenguaje  Python  |  31
Machine Translated by Google

resultados  =  
[]  for  línea  en  file_handle:  #  
mantener  las  líneas  vacías  por  ahora  
#  if  len(línea)  ==  0:  #  
continuar  
resultados.append(línea.reemplazar('foo',  'bar'))

Los  comentarios  también  pueden  aparecer  después  de  una  línea  de  código  ejecutado.  Si  bien  algunos  programadores  
prefieren  que  los  comentarios  se  coloquen  en  la  línea  que  precede  a  una  línea  de  código  en  particular,  esto  puede  ser  útil  
a  veces:

print("Llegué  a  esta  línea")  #  Informe  de  estado  simple

Llamadas  a  métodos  de  funciones  y  objetos

Llamas  a  funciones  usando  paréntesis  y  pasando  cero  o  más  argumentos,  opcionalmente  asignando  el  valor  devuelto  a  
una  variable:

resultado  =  f(x,  y,  z)  g()

Casi  todos  los  objetos  en  Python  tienen  funciones  adjuntas,  conocidas  como  métodos,  que  tienen  acceso  al  contenido  
interno  del  objeto.  Puede  llamarlos  usando  la  siguiente  sintaxis:

obj.some_method(x,  y,  z)

Las  funciones  pueden  tomar  tanto  argumentos  posicionales  como  de  palabras  clave:

resultado  =  f(a,  b,  c,  d=5,  e='foo')
Más  sobre  esto  más  adelante.

Variables  y  paso  de  argumentos  Al  

asignar  una  variable  (o  nombre)  en  Python,  está  creando  una  referencia  al  objeto  en  el  lado  derecho  del  signo  igual.  En  
términos  prácticos,  considere  una  lista  de  números  enteros:

En  [8]:  a  =  [1,  2,  3]

Supongamos  que  asignamos  a  a  una  nueva  variable  b:

En  [9]:  b  =  a

En  algunos  idiomas,  esta  asignación  haría  que  se  copiaran  los  datos  [1,  2,  3] .  En  Python,  a  y  b  en  realidad  ahora  se  
refieren  al  mismo  objeto,  la  lista  original  [1,  2,  3]  (consulte  la  Figura  2­7  para  ver  una  maqueta).  Puedes  comprobarlo  por  ti  
mismo  agregando  un  elemento  a  a  y  luego  examinando  b:

En  [10]:  a.append(4)

En  [11]:  b
Salida[11]:  [1,  2,  3,  4]

32  |  Capítulo  2:  Conceptos  básicos  del  lenguaje  Python,  IPython  y  Jupyter  Notebooks
Machine Translated by Google

Figura  2­7.  Dos  referencias  para  el  mismo  objeto.

Comprender  la  semántica  de  las  referencias  en  Python  y  cuándo,  cómo  y  por  qué  se  copian  los  datos  
es  especialmente  crítico  cuando  trabaja  con  conjuntos  de  datos  más  grandes  en  Python.

La  asignación  también  se  conoce  como  vinculación,  ya  que  vinculamos  un  
nombre  a  un  objeto.  Los  nombres  de  variables  que  se  han  asignado  pueden  
ocasionalmente  denominarse  variables  vinculadas.

Cuando  pasa  objetos  como  argumentos  a  una  función,  se  crean  nuevas  variables  locales  que  hacen  
referencia  a  los  objetos  originales  sin  ninguna  copia.  Si  vincula  un  nuevo  objeto  a  una  variable  dentro  
de  una  función,  ese  cambio  no  se  reflejará  en  el  ámbito  principal.  Por  lo  tanto,  es  posible  alterar  las  
partes  internas  de  un  argumento  mutable.  Supongamos  que  tuviéramos  la  siguiente  función:

def  append_element(alguna_lista,  elemento):  
alguna_lista.append(elemento)
Entonces  nosotros  tenemos:

En  [27]:  datos  =  [1,  2,  3]

En  [28]:  append_element(datos,  4)

En  [29]:  datos
Salida[29]:  [1,  2,  3,  4]

Referencias  dinámicas,  tipos  fuertes  

A  diferencia  de  muchos  lenguajes  compilados,  como  Java  y  C++,  las  referencias  a  objetos  en  Python  
no  tienen  ningún  tipo  asociado.  No  hay  problema  con  lo  siguiente:

En  [12]:  a  =  5

En  [13]:  tipo(a)
Salida[13]:  int

En  [14]:  a  =  'foo'

2.3  Conceptos  básicos  del  lenguaje  Python  |  33
Machine Translated by Google

En  [15]:  tipo(a)
Salida[15]:  calle

Las  variables  son  nombres  de  objetos  dentro  de  un  espacio  de  nombres  particular;  la  información  de  tipo  
se  almacena  en  el  propio  objeto.  Algunos  observadores  podrían  concluir  apresuradamente  que  Python  
no  es  un  "lenguaje  escrito".  Esto  no  es  verdad;  considera  este  ejemplo:

En  [16]:  '5'  +  5
­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­  ­­­­­­­­­­­­­­­­­­­­­­­­­

TypeError   Rastreo  (última  llamada  más  reciente)
<ipython­input­16­f9dbf5f0b234>  en  <módulo>()  ­­­­>  1  '5'  
+  5
TypeError:  debe  ser  str,  no  int

En  algunos  lenguajes,  como  Visual  Basic,  la  cadena  '5'  puede  convertirse  (o  convertir)  implícitamente  en  
un  número  entero,  lo  que  da  como  resultado  10.  Sin  embargo,  en  otros  lenguajes,  como  JavaScript,  el  
número  entero  5  puede  convertirse  en  una  cadena,  lo  que  da  como  resultado  la  cadena  concatenada  
'55'.  En  este  sentido,  Python  se  considera  un  lenguaje  fuertemente  tipado,  lo  que  significa  que  cada  
objeto  tiene  un  tipo  (o  clase)  específico,  y  las  conversiones  implícitas  ocurrirán  solo  en  ciertas  
circunstancias  obvias,  como  las  siguientes:

En  [17]:  a  =  4,5

En  [18]:  b  =  2

#  Formateo  de  cadenas,  que  se  visitará  más  
adelante  En  [19]:  print('a  es  {0},  b  es  {1}'.format(type(a),  type(b)))  a  is  <class  
'float'> ,  b  es  <clase  'int'>

Entrada  [20]:  a /  
b  Salida  [20]:  2,25

Conocer  el  tipo  de  un  objeto  es  importante,  y  es  útil  poder  escribir  funciones  que  puedan  manejar  muchos  
tipos  diferentes  de  entrada.  Puede  verificar  que  un  objeto  es  una  instancia  de  un  tipo  particular  usando  la  
función  isinstance :

En  [21]:  a  =  5

En  [22]:  es  instancia  (a,  int)
Salida[22]:  Verdadero

isinstance  puede  aceptar  una  tupla  de  tipos  si  desea  verificar  que  el  tipo  de  un  objeto  se  encuentra  entre  
los  presentes  en  la  tupla:

En  [23]:  a  =  5;  b  =  4,5

En  [24]:  isinstance(a,  (int,  float))
Salida[24]:  Verdadero

En  [25]:  isinstance(b,  (int,  float))
Salida[25]:  Verdadero

34  |  Capítulo  2:  Conceptos  básicos  del  lenguaje  Python,  IPython  y  Jupyter  Notebooks
Machine Translated by Google

Atributos  y  métodos

Los  objetos  en  Python  suelen  tener  atributos  (otros  objetos  de  Python  almacenados  "dentro"  del  objeto)  y  
métodos  (funciones  asociadas  con  un  objeto  que  pueden  tener  acceso  a  los  datos  internos  del  objeto).  Se  
accede  a  ambos  a  través  de  la  sintaxis  obj.attribute_name:

En  [1]:  a  =  'foo'

En  [2]:  a.<Presione  
Tabulador>  a.en  mayúsculas   a.rindex
a.isupper  a.strip  a.join  
a.rjust  a.swapcase  a.ljust  
a.formato  a.centrar  a.índice   a.rpartition  a.title  a.lower  a.rsplit  a.lstrip  a.rstrip  a.partition  
a.recuento  a.isalnum   a.split  a.replace  a.splitlines  a.  rfind  a.comienza  con
a.decodificar  a.isalpha   a.traducir
a.codificar  a.isdigit  a.termina   a.superior  
con  a.islower a.zfill
a.expandtabs  a.isspace  a.find  
a.istitle

También  se  puede  acceder  a  los  atributos  y  métodos  por  nombre  a  través  de  la  función  getattr :

En  [27]:  getattr(a,  'dividir')
Salida[27]:  <función  str.split>

En  otros  idiomas,  el  acceso  a  los  objetos  por  su  nombre  a  menudo  se  denomina  "reflexión".
Si  bien  no  utilizaremos  de  manera  extensa  las  funciones  getattr  y  las  funciones  relacionadas  hasattr  y  setattr  
en  este  libro,  se  pueden  usar  de  manera  muy  efectiva  para  escribir  código  genérico  y  reutilizable.

Tipificación  

de  pato  A  menudo,  es  posible  que  no  le  importe  el  tipo  de  un  objeto,  sino  solo  si  tiene  ciertos  métodos  o  
comportamiento.  Esto  a  veces  se  denomina  "tipado  de  pato",  después  del  dicho  "Si  camina  como  un  pato  y  
grazna  como  un  pato,  entonces  es  un  pato".  Por  ejemplo,  puede  verificar  que  un  objeto  es  iterable  si  
implementó  el  protocolo  iterador.  Para  muchos  objetos,  esto  significa  que  tiene  un  "método  mágico"  __iter__ ,  
aunque  una  forma  alternativa  y  mejor  de  verificar  es  intentar  usar  la  función  iter :

def  isiterable(obj):  prueba:  

iter(obj)  return  True

excepto  TypeError:  #  no  iterable  return  False

Esta  función  devolvería  True  para  cadenas,  así  como  para  la  mayoría  de  los  tipos  de  colección  de  Python:

En  [29]:  isiterable('una  cadena')
Salida[29]:  Verdadero

En  [30]:  isiterable([1,  2,  3])

2.3  Conceptos  básicos  del  lenguaje  Python  |  35
Machine Translated by Google

Salida[30]:  Verdadero

En  [31]:  isiterable(5)
Salida[31]:  Falso

Un  lugar  donde  uso  esta  funcionalidad  todo  el  tiempo  es  para  escribir  funciones  que  pueden  aceptar  múltiples  
tipos  de  entrada.  Un  caso  común  es  escribir  una  función  que  pueda  aceptar  cualquier  tipo  de  secuencia  (lista,  
tupla,  ndarray)  o  incluso  un  iterador.  Primero  puede  verificar  si  el  objeto  es  una  lista  (o  una  matriz  NumPy)  y,  
si  no  lo  es,  convertirlo  en  uno:

si  no  es  instancia  (x,  lista)  e  isiterable  (x):  x  =  lista  (x)

Importaciones  En  Python,  un  módulo  es  simplemente  un  archivo  con  la  extensión .py  que  contiene  código  de  Python.
Supongamos  que  tuviéramos  el  siguiente  módulo:

#  algún_módulo.py  
PI  =  3.14159

definición  f(x):
volver  x  +  2

definición  g(a,  b):
devolver  a  +  b

Si  quisiéramos  acceder  a  las  variables  y  funciones  definidas  en  some_module.py,  desde  otro  archivo  en  el  
mismo  directorio  podríamos  hacer:

importar  algún_módulo  
resultado  =  algún_módulo.f(5)  pi  
=  algún_módulo.PI

O  equivalente:

from  some_module  importar  f,  g,  PI  
resultado  =  g(5,  PI)

Al  usar  la  palabra  clave  as ,  puede  dar  a  las  importaciones  diferentes  nombres  de  variables:

importar  algún_módulo  como  
sm  desde  algún_módulo  importar  PI  como  pi,  g  como  gf

r1  =  sm.f(pi)  r2  
=  gf(6,  pi)

Operadores  binarios  y  comparaciones

La  mayoría  de  las  operaciones  y  comparaciones  matemáticas  binarias  son  como  cabría  esperar:

En  [32]:  5  ­  7
Salida[32]:  ­2

36  |  Capítulo  2:  Conceptos  básicos  del  lenguaje  Python,  IPython  y  Jupyter  Notebooks
Machine Translated by Google

Entrada  [33]:  12  +  21,5  
Salida[33]:  33,5

En  [34]:  5  <=  2
Salida[34]:  Falso

Consulte  la  Tabla  2­3  para  conocer  todos  los  operadores  binarios  disponibles.

Para  verificar  si  dos  referencias  se  refieren  al  mismo  objeto,  use  la  palabra  clave  is .  is  not  también  es  perfectamente  válido  si  
quieres  comprobar  que  dos  objetos  no  son  iguales:

En  [35]:  a  =  [1,  2,  3]

En  [36]:  b  =  a

En  [37]:  c  =  lista(a)

En  [38]:  a  es  b
Salida[38]:  Verdadero

En  [39]:  a  no  es  c
Salida[39]:  Verdadero

Dado  que  lista  siempre  crea  una  nueva  lista  de  Python  (es  decir,  una  copia),  podemos  estar  seguros  de  que  c  es  distinta  de  

a.  Comparar  con  is  no  es  lo  mismo  que  con  el  operador  == ,  porque  en  este  caso  tenemos:

En  [40]:  a  ==  c
Salida[40]:  Verdadero

Un  uso  muy  común  de  es  y  no  es  para  verificar  si  una  variable  es  Ninguna,  ya  que  solo  hay  una  instancia  de  Ninguna:

En  [41]:  a  =  Ninguno

En  [42]:  a  es  Ninguno
Fuera[42]:  Verdadero

Tabla  2­3.  Operadores  binarios

Operación Descripción

a  +  b Añadir  a  y  b
un­b Restar  b  de  a
a *  b Multiplica  a  por  b

un /  b dividir  a  por  b

un //  segundo Floor­divide  a  por  b,  descartando  cualquier  resto  fraccionario

a **  b Elevar  a  a  la  potencia  b

a  &  b True  si  tanto  a  como  b  son  True;  para  números  enteros,  tome  AND  bit  a  bit

un  |  b Verdadero  si  a  o  b  es  Verdadero;  para  números  enteros,  tome  el  OR  bit  a  bit

a ^  segundo
Para  valores  booleanos,  True  si  aob  es  True,  pero  no  ambos;  para  números  enteros,  tome  el  EXCLUSIVO­OR  bit  a  bit

2.3  Conceptos  básicos  del  lenguaje  Python  |  37
Machine Translated by Google

Operación Descripción

un  ==  segundo Verdadero  si  a  es  igual  a  b

un !=  segundo Verdadero  si  a  no  es  igual  a  b

a  <=  b,  a  <  b  Verdadero  si  a  es  menor  que  (menor  o  igual)  que  
ba  >  b,  a  >=  b  Verdadero  si  a  es  mayor  que  (mayor  que  o  igual)  que  b
a  es  b Verdadero  si  a  y  b  hacen  referencia  al  mismo  objeto  de  Python

a  no  es  b Verdadero  si  a  y  b  hacen  referencia  a  diferentes  objetos  de  Python

Objetos  mutables  e  inmutables  La  

mayoría  de  los  objetos  en  Python,  como  listas,  dictados,  matrices  NumPy  y  la  mayoría  de  los  tipos  
(clases)  definidos  por  el  usuario,  son  mutables.  Esto  significa  que  el  objeto  o  los  valores  que  contienen  
se  pueden  modificar:

En  [43]:  una_lista  =  ['foo',  2,  [4,  5]]

En  [44]:  una_lista[2]  =  (3,  4)

En  [45]:  una_lista
Fuera[45]:  ['foo',  2,  (3,  4)]

Otros,  como  cadenas  y  tuplas,  son  inmutables:

En  [46]:  una_tupla  =  (3,  5,  (4,  5))

En  [47]:  a_tuple[1]  =  'cuatro'
­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­  ­­­­­­­­­­­­­­­­­­­­­­­­­

TypeError   Rastreo  (última  llamada  más  reciente)
<ipython­input­47­b7966a9ae0f1>  en  <módulo>()  ­­­­>  1  
a_tuple[1]  =  'cuatro'
TypeError:  el  objeto  'tupla'  no  admite  la  asignación  de  elementos

Recuerde  que  el  hecho  de  que  pueda  mutar  un  objeto  no  significa  que  siempre  deba  hacerlo.  Tales  
acciones  se  conocen  como  efectos  secundarios.  Por  ejemplo,  al  escribir  una  función,  cualquier  efecto  
secundario  debe  comunicarse  explícitamente  al  usuario  en  la  documentación  o  los  comentarios  de  la  
función.  Si  es  posible,  recomiendo  tratar  de  evitar  los  efectos  secundarios  y  favorecer  la  inmutabilidad,  
aunque  puede  haber  objetos  mutables  involucrados.

Tipos  escalares  

Python,  junto  con  su  biblioteca  estándar,  tiene  un  pequeño  conjunto  de  tipos  incorporados  para  manejar  
datos  numéricos,  cadenas,  valores  booleanos  (verdadero  o  falso)  y  fechas  y  horas.  Estos  tipos  de  
"valor  único"  a  veces  se  denominan  tipos  escalares  y  en  este  libro  nos  referiremos  a  ellos  como  
escalares.  Consulte  la  Tabla  2­4  para  obtener  una  lista  de  los  principales  tipos  de  escalares.  El  manejo  
de  la  fecha  y  la  hora  se  discutirá  por  separado,  ya  que  estos  son  proporcionados  por  el  módulo  de  
fecha  y  hora  en  la  biblioteca  estándar.

38  |  Capítulo  2:  Conceptos  básicos  del  lenguaje  Python,  IPython  y  Jupyter  Notebooks
Machine Translated by Google

Tabla  2­4.  Tipos  escalares  estándar  de  Python

Tipo Descripción

Ninguno  El  valor  "nulo"  de  Python  (solo  existe  una  instancia  del  objeto  Ninguno )

calle tipo  de  cadena;  contiene  cadenas  Unicode  (codificación  UTF­8)

bytes  Bytes  ASCII  sin  procesar  (o  Unicode  codificados  como  bytes)  

float  Número  de  punto  flotante  de  precisión  doble  (64  bits)  (tenga  en  cuenta  que  no  hay  un  tipo  doble  separado )

bool  Un  valor  verdadero  o  falso

En  t Entero  con  signo  de  precisión  arbitraria

Tipos  numéricos  

Los  tipos  primarios  de  Python  para  números  son  int  y  float.  Un  int  puede  almacenar  números  
arbitrariamente  grandes:

En  [48]:  ival  =  17239871

En  [49]:  val  **  6
Salida[49]:  26254519291092456596965462913230729701102721

Los  números  de  punto  flotante  se  representan  con  el  tipo  flotante  de  Python .  Debajo  del  capó,  cada  uno  
es  un  valor  de  doble  precisión  (64  bits).  También  se  pueden  expresar  con  notación  científica:

En  [50]:  valorf  =  7.243

En  [51]:  fval2  =  6.78e­5

La  división  de  enteros  que  no  da  como  resultado  un  número  entero  siempre  producirá  un  número  de  
coma  flotante:

Entrada  [52]:  3 /  2  
Salida  [52]:  1,5

Para  obtener  una  división  de  enteros  al  estilo  C  (que  descarta  la  parte  fraccionaria  si  el  resultado  no  es  
un  número  entero),  use  el  operador  de  división  de  piso //:

En  [53]:  3 //  2
Salida[53]:  1

Cadenas  Mucha  gente  usa  Python  por  sus  poderosas  y  flexibles  capacidades  integradas  de  
' o  comillas  
procesamiento  de  cadenas.  Puede  escribir  literales  de  cadena  usando  comillas  simples
dobles  ":

a  =  'una  forma  de  escribir  una  cadena'  b  =  
"otra  forma"

Para  cadenas  de  varias  líneas  con  saltos  de  línea,  puede  usar  comillas  triples,  ya  sea  '''  o  """:

2.3  Conceptos  básicos  del  lenguaje  Python  |  39
Machine Translated by Google

c  = """
Esta  es  una  cadena  más  larga  que  
abarca  varias  líneas.
"""

Puede  que  le  sorprenda  que  esta  cadena  c  en  realidad  contiene  cuatro  líneas  de  texto;  la  línea  se  rompe  
después  de  """  y  las  líneas  posteriores  se  incluyen  en  la  cadena.  Podemos  contar  los  caracteres  de  la  nueva  
línea  con  el  método  de  conteo  en  c:

En  [55]:  c.cuenta('\n')
Salida[55]:  3

Las  cadenas  de  Python  son  inmutables;  no  se  puede  modificar  una  cadena:

En  [56]:  a  =  'esto  es  una  cadena'

En  [57]:  a[10]  =  'f'
­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­  ­­­­­­­­­­­­­­­­­­­­­­­­­

TypeError   Rastreo  (última  llamada  más  reciente)
<ipython­input­57­5ca625d1e504>  en  <módulo>()  ­­­­>  1  
a[10]  =  'f'
TypeError:  el  objeto  'str'  no  admite  la  asignación  de  elementos

En  [58]:  b  =  a.replace('cadena',  'cadena  más  larga')

En  [59]:  b
Out[59]:  'esta  es  una  cadena  más  larga'

Después  de  esta  operación,  la  variable  a  no  se  modifica:

En  [60]:  un
Out[60]:  'esto  es  una  cadena'

Muchos  objetos  de  Python  se  pueden  convertir  en  una  cadena  usando  la  función  str :

En  [61]:  a  =  5.6

En  [62]:  s  =  cadena  (a)

En  [63]:  impresión(es)  
5.6

Las  cadenas  son  una  secuencia  de  caracteres  Unicode  y,  por  lo  tanto,  pueden  tratarse  como  otras  secuencias,  
como  listas  y  tuplas  (que  exploraremos  con  más  detalle  en  el  próximo  capítulo):

En  [64]:  s  =  'pitón'

En  [65]:  lista(s)
Salida[65]:  ['p',  'y',  't',  'h',  'o',  'n']

En  [66]:  s[:3]
Salida[66]:  'pyt'

40  |  Capítulo  2:  Conceptos  básicos  del  lenguaje  Python,  IPython  y  Jupyter  Notebooks
Machine Translated by Google

La  sintaxis  s[:3]  se  llama  rebanar  y  se  implementa  para  muchos  tipos  de  secuencias  de  Python.  Esto  se  
explicará  con  más  detalle  más  adelante,  ya  que  se  usa  ampliamente  en  este  libro.

El  carácter  de  barra  invertida  \  es  un  carácter  de  escape,  lo  que  significa  que  se  utiliza  para  especificar  
caracteres  especiales  como  nueva  línea  \n  o  caracteres  Unicode.  Para  escribir  un  literal  de  cadena  con  
barras  diagonales  inversas,  debe  escapar  de  ellas:

En  [67]:  s  =  '12\\34'

En  [68]:  imprimir(s)  
12\34

Si  tiene  una  cadena  con  muchas  barras  invertidas  y  sin  caracteres  especiales,  puede  que  le  resulte  un  
poco  molesto.  Afortunadamente,  puede  anteponer  la  comilla  inicial  de  la  cadena  con  r,  lo  que  significa  que  
los  caracteres  deben  interpretarse  como  son:

En  [69]:  s  =  r'esto\no\tiene\caracteres\especiales'

En  [70]:  s
Out[70]:  'esto\\no\tiene\\personajes\especiales'

La  r  significa  crudo.

Agregar  dos  cadenas  juntas  las  concatena  y  produce  una  nueva  cadena:

En  [71]:  a  =  'esta  es  la  primera  mitad'

En  [72]:  b  =  'y  esta  es  la  segunda  mitad'

En  [73]:  a  +  b
Out[73]:  'esta  es  la  primera  mitad  y  esta  es  la  segunda  mitad'

La  creación  de  plantillas  o  el  formato  de  cadenas  es  otro  tema  importante.  La  cantidad  de  formas  de  
hacerlo  se  ha  ampliado  con  la  llegada  de  Python  3,  y  aquí  describiré  brevemente  la  mecánica  de  una  de  
las  interfaces  principales.  Los  objetos  de  cadena  tienen  un  método  de  formato  que  se  puede  usar  para  
sustituir  argumentos  formateados  en  la  cadena,  produciendo  una  nueva  cadena:

En  [74]:  template  =  '{0:.2f}  {1:s}  valen  US${2:d}'

En  esta  cadena,

•  {0:.2f}  significa  formatear  el  primer  argumento  como  un  número  de  coma  flotante  con  dos
lugares  decimales.

•  {1:s}  significa  formatear  el  segundo  argumento  como  una  cadena.  •  

{2:d}  significa  formatear  el  tercer  argumento  como  un  entero  exacto.

Para  sustituir  argumentos  por  estos  parámetros  de  formato,  pasamos  una  secuencia  de  argumentos  al  
método  de  formato :

2.3  Conceptos  básicos  del  lenguaje  Python  |  41
Machine Translated by Google

En  [75]:  template.format(4.5560,  'Pesos  argentinos',  1)
Fuera[75]:  '4,56  pesos  argentinos  valen  US$1'

El  formato  de  cadenas  es  un  tema  profundo;  hay  múltiples  métodos  y  numerosas  opciones  y  ajustes  disponibles  
para  controlar  cómo  se  formatean  los  valores  en  la  cadena  resultante.  Para  obtener  más  información,  recomiendo  
consultar  la  documentación  oficial  de  Python.

Discuto  el  procesamiento  general  de  cadenas  en  relación  con  el  análisis  de  datos  con  más  detalle  en  el  Capítulo
ter  8.

Bytes  y  Unicode  En  

Python  moderno  (es  decir,  Python  3.0  y  superior),  Unicode  se  ha  convertido  en  el  tipo  de  cadena  de  primera  
clase  para  permitir  un  manejo  más  consistente  de  texto  ASCII  y  no  ASCII.  En  versiones  anteriores  de  Python,  
las  cadenas  eran  solo  bytes  sin  ninguna  codificación  Unicode  explícita.  Puede  convertir  a  Unicode  suponiendo  
que  conozca  la  codificación  de  caracteres.  Veamos  un  ejemplo:

En  [76]:  val  =  "español"

En  [77]:  valor
Fuera[77]:  'español'

Podemos  convertir  esta  cadena  Unicode  a  su  representación  de  bytes  UTF­8  utilizando  el  método  de  codificación :

En  [78]:  val_utf8  =  val.encode('utf­8')

En  [79]:  val_utf8
Fuera[79]:  b'espa\xc3\xb1ol'

En  [80]:  tipo  (val_utf8)
Salida[80]:  bytes

Suponiendo  que  conoce  la  codificación  Unicode  de  un  objeto  de  bytes ,  puede  volver  atrás  usando  el  método  
de  decodificación :

En  [81]:  val_utf8.decode('utf­8')
Fuera[81]:  'español'

Si  bien  se  prefiere  usar  UTF­8  para  cualquier  codificación,  por  razones  históricas  es  posible  que  encuentre  datos  
en  varias  codificaciones  diferentes:

En  [82]:  val.encode('latin1')
Fuera[82]:  b'espa\xf1ol'

En  [83]:  val.encode('utf­16')
Salida[83]:  b'\xff\xfee\x00s\x00p\x00a\x00\xf1\x00o\x00l\x00'

En  [84]:  val.encode('utf­16le')
Salida[84]:  b'e\x00s\x00p\x00a\x00\xf1\x00o\x00l\x00'

42  |  Capítulo  2:  Conceptos  básicos  del  lenguaje  Python,  IPython  y  Jupyter  Notebooks
Machine Translated by Google

Es  más  común  encontrar  objetos  de  bytes  en  el  contexto  de  trabajar  con  archivos,  donde  es  
posible  que  no  se  desee  decodificar  implícitamente  todos  los  datos  en  cadenas  Unicode.

Aunque  es  posible  que  rara  vez  necesite  hacerlo,  puede  definir  sus  propios  literales  de  bytes  
prefijando  una  cadena  con  b:

En  [85]:  bytes_val  =  b'esto  es  bytes'

En  [86]:  bytes_val
Salida[86]:  b'esto  son  bytes'

En  [87]:  decodificado  =  bytes_val.decode('utf8')

En  [88]:  decodificado  #  esto  es  str  (Unicode)  ahora
Out[88]:  'esto  es  bytes'

Booleanos

Los  dos  valores  booleanos  en  Python  se  escriben  como  Verdadero  y  Falso.  Las  comparaciones  y  
otras  expresiones  condicionales  se  evalúan  como  Verdadero  o  Falso.  Los  valores  booleanos  se  
combinan  con  las  palabras  clave  and  y  or :

En  [89]:  Verdadero  y  Verdadero
Salida[89]:  Verdadero

En  [90]:  Falso  o  Verdadero
Salida[90]:  Verdadero

Tipo  de  fundición

Los  tipos  str,  bool,  int  y  float  también  son  funciones  que  se  pueden  usar  para  convertir  
valores  a  esos  tipos:
En  [91]:  s  =  '3.14159'

En  [92]:  fval  =  flotante(s)

En  [93]:  tipo  (fval)
Salida[93]:  flotante

En  [94]:  int(fval)
Salida[94]:  3

En  [95]:  booleano(fval)
Fuera[95]:  Verdadero

En  [96]:  bool(0)
Salida[96]:  Falso

2.3  Conceptos  básicos  del  lenguaje  Python  |  43
Machine Translated by Google

Ninguno

Ninguno  es  el  tipo  de  valor  nulo  de  Python.  Si  una  función  no  devuelve  explícitamente  un  valor,  implícitamente  devuelve  
Ninguno:

En  [97]:  a  =  Ninguno

En  [98]:  a  es  Ninguno
Salida[98]:  Verdadero

En  [99]:  b  =  5

En  [100]:  b  no  es  Ninguno
Salida[100]:  Verdadero

Ninguno  también  es  un  valor  predeterminado  común  para  los  argumentos  de  función:

def  sumar_y_tal  vez_multiplicar(a,  b,  c=Ninguno):  resultado  =  
a  +  b

si  c  no  es  Ninguno:  
resultado  =  resultado  * C

resultado  devuelto

Si  bien  es  un  punto  técnico,  vale  la  pena  tener  en  cuenta  que  None  no  es  solo  una  palabra  clave  reservada,  sino  también  
una  instancia  única  de  NoneType:

En  [101]:  tipo  (Ninguno)
Salida[101]:  NingunoTipo

Fechas  y  horas

El  módulo  integrado  de  fecha  y  hora  de  Python  proporciona  tipos  de  fecha  y  hora,  fecha  y  hora .  El  tipo  datetime ,  como  
puedes  imaginar,  combina  la  información  almacenada  en  date  y  time  y  es  el  más  utilizado:

In  [102]:  from  datetime  import  datetime,  date,  time

En  [103]:  dt  =  fecha  y  hora  (2011,  10,  29,  20,  30,  21)

Entrada  [104]:  dt.día  
Salida[104]:  29

Entrada  [105]:  dt.minuto  
Salida[105]:  30

Dada  una  instancia  de  fecha  y  hora ,  puede  extraer  los  objetos  de  fecha  y  hora  equivalentes  
llamando  a  métodos  en  la  fecha  y  hora  del  mismo  nombre:

En  [106]:  dt.fecha()
Salida[106]:  fechahora.fecha(2011,  10,  29)

44  |  Capítulo  2:  Conceptos  básicos  del  lenguaje  Python,  IPython  y  Jupyter  Notebooks
Machine Translated by Google

En  [107]:  dt.time()
Salida[107]:  fechahora.hora(20,  30,  21)

El  método  strftime  formatea  una  fecha  y  hora  como  una  cadena:

En  [108]:  dt.strftime('%m/%d/%Y  %H:%M')
Salida[108]:  '29/10/2011  20:30'

Las  cadenas  se  pueden  convertir  (analizar)  en  objetos  de  fecha  y  hora  con  la  función  strptime :

En  [109]:  fechahora.strptime('20091031',  '%Y%m%d')
Salida[109]:  fechahora.fechahora(2009,  10,  31,  0,  0)

Consulte  la  Tabla  2­5  para  obtener  una  lista  completa  de  las  especificaciones  de  formato.

Cuando  esté  agregando  o  agrupando  datos  de  series  temporales,  ocasionalmente  será  útil  reemplazar  
los  campos  de  tiempo  de  una  serie  de  fechas  y  horas,  por  ejemplo,  reemplazar  los  campos  de  minutos  y  
segundos  con  cero:

En  [110]:  dt.replace(minuto=0,  segundo=0)
Salida[110]:  fechahora.fechahora(2011,  10,  29,  20,  0)

Dado  que  datetime.datetime  es  un  tipo  inmutable,  métodos  como  estos  siempre  producen  nuevos  objetos.

La  diferencia  de  dos  objetos  de  fecha  y  hora  produce  un  tipo  datetime.timedelta :

En  [111]:  dt2  =  fecha  y  hora  (2011,  11,  15,  22,  30)

En  [112]:  delta  =  dt2  ­  dt

Entrada  [113]:  
delta  Salida[113]:  fechahora.timedelta(17,  7179)

En  [114]:  tipo  (delta)
Salida[114]:  fechahora.timedelta

El  timedelta  de  salida  (17,  7179)  indica  que  el  timedelta  codifica  un  desplazamiento  de  17  días  y  7179  
segundos.

Agregar  un  timedelta  a  una  fecha  y  hora  produce  una  nueva  fecha  y  hora  cambiada :

Entrada  [115]:  
dt  Salida[115]:  fechahora.fechahora(2011,  10,  29,  20,  30,  21)

Entrada  [116]:  dt  +  delta  
Salida  [116]:  datetime.datetime(2011,  11,  15,  22,  30)

Tabla  2­5.  Especificación  de  formato  de  fecha  y  hora  (compatible  con  ISO  C89)

Tipo  Descripción

%Y  Año  de  cuatro  

dígitos  %y  Año  de  dos  dígitos

2.3  Conceptos  básicos  del  lenguaje  Python  |  45
Machine Translated by Google

Tipo  Descripción  %m  

Mes  de  dos  dígitos  [01,  12]  %d  Día  de  dos  

dígitos  [01,  31]

%H  Hora  (reloj  de  24  horas)  [00,  23]

%I  Hora  (reloj  de  12  horas)  [01,  12]

%M  Minuto  de  dos  dígitos  [00,  59]

%S  Segundo  [00,  61]  (los  segundos  60,  61  representan  los  segundos  bisiestos)  %w  

Día  de  la  semana  como  número  entero  [0  (domingo),  6]

%U  Número  de  semana  del  año  [00,  53];  El  domingo  se  considera  el  primer  día  de  la  semana,  y  los  días  anteriores  al  primer  domingo  del  año  son  “semana  0”

%W  Número  de  semana  del  año  [00,  53];  Se  considera  lunes  el  primer  día  de  la  semana,  y  los  días  anteriores  al  primer  lunes  de

el  año  es  "semana  0"  %z  

desplazamiento  de  zona  horaria  UTC  como  +HHMM  o  ­HHMM;  vacío  si  la  zona  horaria  es  ingenua  

%F  Atajo  para  %Y­%m­%d  (p.  ej.,  2012­4­18)

Acceso  directo  a  %D  para  %m/%d/%y  (p.  ej.,  18/04/12)

Flujo  de  control

Python  tiene  varias  palabras  clave  integradas  para  lógica  condicional,  bucles  y  otros  conceptos  de  flujo  
de  control  estándar  que  se  encuentran  en  otros  lenguajes  de  programación.

si,  elif,  y  más
La  sentencia  if  es  uno  de  los  tipos  de  sentencias  de  flujo  de  control  más  conocidas.  Comprueba  una  
condición  que,  si  es  Verdadera,  evalúa  el  código  en  el  bloque  que  sigue:

si  x  <  0:  
print('Es  negativo')

Una  declaración  if  puede  ser  seguida  opcionalmente  por  uno  o  más  bloques  elif  y  un  bloque  catch  all  
else  si  todas  las  condiciones  son  falsas:

si  x  <  0:  
print('Es  negativo')
elif  x  ==  0:
print('Igual  a  cero')  elif  0  <  
x  <  5:  print('Positivo  
pero  menor  que  5')  else:  print('Positivo  y  
mayor  
o  igual  a  5')

Si  alguna  de  las  condiciones  es  verdadera,  no  se  alcanzarán  más  bloques  elif  o  else .  Con  una  condición  
compuesta  usando  y  o  o,  las  condiciones  se  evalúan  de  izquierda  a  derecha  y  provocarán  un  cortocircuito:

En  [117]:  a  =  5;  b  =  7

En  [118]:  c  =  8;  re  =  4

46  |  Capítulo  2:  Conceptos  básicos  del  lenguaje  Python,  IPython  y  Jupyter  Notebooks
Machine Translated by Google

En  [119]:  si  a  <  b  o  c  >  d:  print('Hecho  ')
.....:
lo  hizo

En  este  ejemplo,  la  comparación  c  >  d  nunca  se  evalúa  porque  la  primera  comparación
ison  era  Verdadero.

También  es  posible  encadenar  comparaciones:

En  [120]:  4  >  3  >  2  >  1
Salida[120]:  Verdadero

for  loops  

for  loops  son  para  iterar  sobre  una  colección  (como  una  lista  o  tupla)  o  un  iterador.  La  sintaxis  estándar  para  un  bucle  
for  es:

por  valor  en  la  colección:
#  hacer  algo  con  valor

Puede  avanzar  un  bucle  for  a  la  siguiente  iteración,  omitiendo  el  resto  del  bloque,  usando  la  palabra  clave  continuar .  
Considere  este  código,  que  suma  los  números  enteros  en  una  lista  y  omite  los  valores  Ninguno :

secuencia  =  [1,  2,  Ninguno,  4,  Ninguno,  5]
totales  =  0
para  el  valor  en  secuencia:  
si  el  valor  es  Ninguno:  
continuar
total  +=  valor

Se  puede  salir  de  un  bucle  for  por  completo  con  la  palabra  clave  break .  Este  código  suma  los  elementos  de  la  lista  
hasta  llegar  a  un  5:

secuencia  =  [1,  2,  0,  4,  6,  5,  2,  1]  total_hasta_5  =  0  
para  valor  en  secuencia:  
si  valor  ==  5:  romper  
total_hasta_5  +=  
valor

La  palabra  clave  break  solo  termina  el  bucle  for  más  interno ;  cualquier  bucle  for  externo
seguir  corriendo:

En  [121]:  for  i  in  range(4):  for  j  in  range(4):  
.....: if  j  >  i:  break  print((i,  j))
.....:
.....:
.....:
.....:
(0,  0)  
(1,  0)

2.3  Conceptos  básicos  del  lenguaje  Python  |  47
Machine Translated by Google

(1,  1)  
(2,  0)  
(2,  1)  
(2,  2)  
(3,  0)  
(3,  1)  
(3 ,  2)  
(3,  3)

Como  veremos  con  más  detalle,  si  los  elementos  en  la  colección  o  el  iterador  son  secuencias  (tuplas  o  
listas,  por  ejemplo),  se  pueden  descomprimir  convenientemente  en  variables  en  la  declaración  del  bucle  
for :

para  a,  b,  c  en  el  iterador:  #  hacer  
algo

mientras  que  los  bucles

Un  bucle  while  especifica  una  condición  y  un  bloque  de  código  que  se  ejecutará  hasta  que  la  condición  se  
evalúe  como  Falsa  o  el  bucle  finalice  explícitamente  con  una  interrupción:

x  =  256
total  =  0  
mientras  x  >  0:  si  
total  >  500:  romper

total  +=  x
x  =  x //  2

aprobar

pass  es  la  declaración  "no­op"  en  Python.  Se  puede  utilizar  en  bloques  en  los  que  no  se  va  a  realizar  
ninguna  acción  (o  como  marcador  de  posición  para  el  código  que  aún  no  se  ha  implementado);  solo  se  
requiere  porque  Python  usa  espacios  en  blanco  para  delimitar  bloques:

si  x  <  0:  
print('negativo!')  elif  x  ==  
0:
#  TODO:  poner  algo  inteligente  aquí
pase  
else:  
print('¡positivo!')

rango

La  función  de  rango  devuelve  un  iterador  que  produce  una  secuencia  de  enteros  espaciados  uniformemente:

En  [122]:  rango  (10)
Salida[122]:  rango(0,  10)

48  |  Capítulo  2:  Conceptos  básicos  del  lenguaje  Python,  IPython  y  Jupyter  Notebooks
Machine Translated by Google

En  [123]:  lista(rango(10))
Salida[123]:  [0,  1,  2,  3,  4,  5,  6,  7,  8,  9]

Se  puede  dar  un  inicio,  un  final  y  un  paso  (que  puede  ser  negativo):

En  [124]:  lista(rango(0,  20,  2))
Salida[124]:  [0,  2,  4,  6,  8,  10,  12,  14,  16,  18]

En  [125]:  lista  (rango  (5,  0,  ­1))
Salida[125]:  [5,  4,  3,  2,  1]

Como  puede  ver,  el  rango  produce  números  enteros  hasta  el  punto  final,  pero  sin  incluirlo.  Un  uso  común  
de  rango  es  para  iterar  a  través  de  secuencias  por  índice:

seq  =  [1,  2,  3,  4]  for  i  
in  range(len(seq)):  val  =  seq[i]

Si  bien  puede  usar  funciones  como  lista  para  almacenar  todos  los  enteros  generados  por  rango  en  alguna  
otra  estructura  de  datos,  a  menudo  la  forma  de  iterador  predeterminada  será  la  que  desee.  Este  fragmento  
suma  todos  los  números  del  0  al  99  999  que  son  múltiplos  de  3  o  5:

suma  =  0
for  i  in  range(100000):  #  %  
es  el  operador  de  módulo  si  i  %  
3  ==  0  o  i  %  5  ==  0:
suma  +=  yo

Si  bien  el  rango  generado  puede  ser  arbitrariamente  grande,  el  uso  de  la  memoria  en  un  momento  dado  
puede  ser  muy  pequeño.

Expresiones  ternarias  

Una  expresión  ternaria  en  Python  le  permite  combinar  un  bloque  if­else  que  produce  un  valor  en  una  sola  
línea  o  expresión.  La  sintaxis  para  esto  en  Python  es:

valor  =  true­expr  si  la  condición  es  false­expr

Aquí,  true­expr  y  false­expr  pueden  ser  cualquier  expresión  de  Python.  Tiene  el  mismo  efecto  que  el  más  
detallado:

si  condición:  
valor  =  true­expr
demás:
valor  =  false­expr

Este  es  un  ejemplo  más  concreto:

En  [126]:  x  =  5

En  [127]:  'No  negativo'  si  x  >=  0  sino  'Negativo'
Salida[127]:  'No  negativo'

2.3  Conceptos  básicos  del  lenguaje  Python  |  49
Machine Translated by Google

Al  igual  que  con  los  bloques  if­else ,  solo  se  ejecutará  una  de  las  expresiones.  Por  lo  tanto,  
los  lados  "if"  y  "else"  de  la  expresión  ternaria  podrían  contener  cálculos  costosos,  pero  solo  se  
evalúa  la  rama  verdadera.

Si  bien  puede  ser  tentador  usar  siempre  expresiones  ternarias  para  condensar  su  código,  
tenga  en  cuenta  que  puede  sacrificar  la  legibilidad  si  la  condición,  así  como  las  expresiones  
verdadera  y  falsa,  son  muy  complejas.

50  |  Capítulo  2:  Conceptos  básicos  del  lenguaje  Python,  IPython  y  Jupyter  Notebooks
Machine Translated by Google

CAPÍTULO  3

Estructuras  de  datos,  funciones  y  archivos  
incorporados

Este  capítulo  analiza  las  capacidades  integradas  en  el  lenguaje  Python  que  se  usarán  de  manera  ubicua  a  lo  
largo  del  libro.  Si  bien  las  bibliotecas  complementarias  como  pandas  y  NumPy  agregan  funcionalidad  
computacional  avanzada  para  conjuntos  de  datos  más  grandes,  están  diseñadas  para  usarse  junto  con  las  
herramientas  de  manipulación  de  datos  integradas  de  Python.

Comenzaremos  con  las  estructuras  de  datos  del  caballo  de  batalla  de  Python:  tuplas,  listas,  dictados  y  
conjuntos.  Luego,  discutiremos  la  creación  de  sus  propias  funciones  de  Python  reutilizables.  Finalmente,  
veremos  la  mecánica  de  los  objetos  de  archivo  de  Python  y  la  interacción  con  su  disco  duro  local.

3.1  Estructuras  de  datos  y  secuencias
Las  estructuras  de  datos  de  Python  son  simples  pero  poderosas.  Dominar  su  uso  es  una  parte  fundamental  
para  convertirse  en  un  programador  de  Python  competente.

Tupla  

Una  tupla  es  una  secuencia  inmutable  y  de  longitud  fija  de  objetos  de  Python.  La  forma  más  fácil  de  crear  
uno  es  con  una  secuencia  de  valores  separados  por  comas:

En  [1]:  tup  =  4,  5,  6

En  [2]:  sup
Salida[2]:  (4,  5,  6)

Cuando  está  definiendo  tuplas  en  expresiones  más  complicadas,  a  menudo  es  necesario  encerrar  los  valores  
entre  paréntesis,  como  en  este  ejemplo  de  creación  de  una  tupla  de  tuplas:

51
Machine Translated by Google

En  [3]:  nested_tup  =  (4,  5,  6),  (7,  8)

En  [4]:  nested_tup
Salida[4]:  ((4,  5,  6),  (7,  8))

Puede  convertir  cualquier  secuencia  o  iterador  en  una  tupla  invocando  la  tupla:

En  [5]:  tupla([4,  0,  2])
Salida[5]:  (4,  0,  2)

En  [6]:  tup  =  tupla('cadena')

En  [7]:  sup
Salida[7]:  ('s',  't',  'r',  'i',  'n',  'g')

Se  puede  acceder  a  los  elementos  con  corchetes  []  como  con  la  mayoría  de  los  otros  tipos  de  secuencia.
Al  igual  que  en  C,  C++,  Java  y  muchos  otros  lenguajes,  las  secuencias  están  indexadas  en  0  en  Python:

En  [8]:  tup[0]
Salida[8]:  's'

Si  bien  los  objetos  almacenados  en  una  tupla  pueden  ser  mutables,  una  vez  que  se  crea  la  tupla,  no  es  
posible  modificar  qué  objeto  se  almacena  en  cada  ranura:

En  [9]:  tup  =  tupla(['foo',  [1,  2],  True])

En  [10]:  tup[2]  =  Falso
­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­  ­­­­­­­­­­­­­­­­­­­­­­­­­

TypeError   Rastreo  (última  llamada  más  reciente)
<ipython­input­10­c7308343b841>  en  <módulo>()  ­­­­>  1  
tup[2]  =  Falso
TypeError:  el  objeto  'tupla'  no  admite  la  asignación  de  elementos

Si  un  objeto  dentro  de  una  tupla  es  mutable,  como  una  lista,  puede  modificarlo  en  el  lugar:

En  [11]:  tup[1].append(3)

En  [12]:  tup
Salida[12]:  ('foo',  [1,  2,  3],  Verdadero)

Puede  concatenar  tuplas  usando  el  operador  +  para  producir  tuplas  más  largas:

En  [13]:  (4,  Ninguno,  'foo')  +  (6,  0)  +  ('bar',)
Salida[13]:  (4,  Ninguno,  'foo',  6,  0,  'bar')

Multiplicar  una  tupla  por  un  número  entero,  como  con  las  listas,  tiene  el  efecto  de  concatenar  juntas  tantas  
copias  de  la  tupla:

En  [14]:  ('foo',  'barra')  *  4
Salida[14]:  ('foo',  'bar',  'foo',  'bar',  'foo',  'bar',  'foo',  'bar')

Tenga  en  cuenta  que  los  objetos  en  sí  no  se  copian,  solo  las  referencias  a  ellos.

52  |  Capítulo  3:  Estructuras  de  datos,  funciones  y  archivos  integrados
Machine Translated by Google

Desempaquetando  tuplas

Si  intenta  asignar  una  expresión  de  variables  similar  a  una  tupla,  Python  intentará  desempaquetar  el  valor  en  el  lado  
derecho  del  signo  igual:

En  [15]:  tup  =  (4,  5,  6)

En  [16]:  a,  b,  c  =  tup

En  [17]:  b
Salida[17]:  5

Incluso  las  secuencias  con  tuplas  anidadas  se  pueden  desempaquetar:

En  [18]:  tup  =  4,  5,  (6,  7)

En  [19]:  a,  b,  (c,  d)  =  tup

En  [20]:  re
Salida[20]:  7

Con  esta  función,  puede  cambiar  fácilmente  los  nombres  de  las  variables,  una  tarea  que  en  muchos  idiomas  podría  
verse  así:

tmp  =  aa  
=  b
b  =  tmp

Pero,  en  Python,  el  intercambio  se  puede  hacer  así:

En  [21]:  a,  b  =  1,  2

En  [22]:  un
Salida[22]:  1

En  [23]:  b
Salida[23]:  2

En  [24]:  b,  a  =  a,  b

En  [25]:  un
Salida[25]:  2

En  [26]:  b
Salida[26]:  1

Un  uso  común  del  desempaquetado  de  variables  es  iterar  sobre  secuencias  de  tuplas  o  listas:

En  [27]:  secuencia  =  [(1,  2,  3),  (4,  5,  6),  (7,  8,  9)]

En  [28]:  para  a,  b,  c  in  seq:  print('a={0},  
c=6   b={1},  c={2}'.format(a,  b,  c)) ....:  a=1,  b=2,  c=3  a=4,  b=5,  
a=7,  b=8,  c=9

3.1  Estructuras  de  datos  y  secuencias  |  53
Machine Translated by Google

Otro  uso  común  es  devolver  múltiples  valores  de  una  función.  Cubriré  esto  con  más  detalle  más  
adelante.

El  lenguaje  Python  adquirió  recientemente  un  desempaquetado  de  tupla  más  avanzado  para  
ayudar  con  situaciones  en  las  que  es  posible  que  desee  "arrancar"  algunos  elementos  desde  el  
principio  de  una  tupla.  Esto  usa  la  sintaxis  especial  *rest,  que  también  se  usa  en  firmas  de  
funciones  para  capturar  una  lista  arbitrariamente  larga  de  argumentos  posicionales:

En  [29]:  valores  =  1,  2,  3,  4,  5

En  [30]:  a,  b,  *resto  =  valores

En  [31]:  a,  b
Salida[31]:  (1,  2)

En  [32]:  descanso
Salida[32]:  [3,  4,  5]

Este  bit  de  descanso  a  veces  es  algo  que  desea  descartar;  no  hay  nada  especial  en  el  resto  del  
nombre.  Como  cuestión  de  convención,  muchos  programadores  de  Python  utilizarán  el  guión  bajo  
(_)  para  las  variables  no  deseadas:

En  [33]:  a,  b,  *_  =  valores

Métodos  de  

tupla  Dado  que  el  tamaño  y  el  contenido  de  una  tupla  no  se  pueden  modificar,  es  muy  ligero  en  los  
métodos  de  instancia.  Uno  particularmente  útil  (también  disponible  en  listas)  es  count,  que  cuenta  
el  número  de  ocurrencias  de  un  valor:

En  [34]:  a  =  (1,  2,  2,  2,  3,  4,  2)

En  [35]:  a.cuenta(2)
Salida[35]:  4

Lista

A  diferencia  de  las  tuplas,  las  listas  son  de  longitud  variable  y  su  contenido  se  puede  modificar  en  
el  lugar.  Puede  definirlos  usando  corchetes  []  o  usando  la  función  de  tipo  de  lista :

En  [36]:  a_list  =  [2,  3,  7,  Ninguno]

En  [37]:  tup  =  ('foo',  'bar',  'baz')

En  [38]:  b_list  =  list(tup)

En  [39]:  b_lista
Salida[39]:  ['foo',  'bar',  'baz']

En  [40]:  b_list[1]  =  'cucú'

54  |  Capítulo  3:  Estructuras  de  datos,  funciones  y  archivos  integrados
Machine Translated by Google

En  [41]:  lista_b
Fuera[41]:  ['foo',  'peekaboo',  'baz']

Las  listas  y  las  tuplas  son  semánticamente  similares  (aunque  las  tuplas  no  se  pueden  modificar)  y  se  
pueden  usar  indistintamente  en  muchas  funciones.

La  función  de  lista  se  usa  con  frecuencia  en  el  procesamiento  de  datos  como  una  forma  de  materializar  
una  expresión  iteradora  o  generadora:

En  [42]:  gen  =  rango  (10)

En  [43]:  gen
Salida[43]:  rango(0,  10)

En  [44]:  lista  (gen)
Salida[44]:  [0,  1,  2,  3,  4,  5,  6,  7,  8,  9]

Adición  y  eliminación  de  elementos.

Los  elementos  se  pueden  agregar  al  final  de  la  lista  con  el  método  de  agregar :

En  [45]:  b_list.append('enano')

En  [46]:  b_lista
Fuera[46]:  ['foo',  'peekaboo',  'baz',  'enano']

Usando  insertar  puede  insertar  un  elemento  en  una  ubicación  específica  en  la  lista:

En  [47]:  b_list.insert(1,  'rojo')

En  [48]:  b_lista
Out[48]:  ['foo',  'red',  'peekaboo',  'baz',  'dwarf']

El  índice  de  inserción  debe  estar  entre  0  y  la  longitud  de  la  lista,  inclusive.

insert  es  computacionalmente  costoso  en  comparación  con  append,  
porque  las  referencias  a  los  elementos  subsiguientes  deben  cambiarse  
internamente  para  dejar  espacio  para  el  nuevo  elemento.  Si  necesita  
insertar  elementos  tanto  al  principio  como  al  final  de  una  secuencia,  es  
posible  que  desee  explorar  collections.deque,  una  cola  de  dos  extremos,  
para  este  propósito.

La  operación  inversa  para  insertar  es  pop,  que  elimina  y  devuelve  un  elemento  en  un  índice  particular:

En  [49]:  b_list.pop(2)
Fuera[49]:  'peekaboo'

En  [50]:  b_lista
Fuera[50]:  ['foo',  'rojo',  'baz',  'enano']

3.1  Estructuras  de  datos  y  secuencias  |  55
Machine Translated by Google

Los  elementos  se  pueden  eliminar  por  valor  con  remove,  que  localiza  el  primer  valor  y  lo  elimina  del  último:

En  [51]:  b_list.append('foo')

En  [52]:  b_lista
Fuera[52]:  ['foo',  'rojo',  'baz',  'enano',  'foo']

En  [53]:  b_list.remove('foo')

En  [54]:  b_lista
Fuera[54]:  ['rojo',  'baz',  'enano',  'foo']

Si  el  rendimiento  no  es  una  preocupación,  al  usar  agregar  y  eliminar,  puede  usar  una  lista  de  Python  como  una  
estructura  de  datos  de  "conjunto  múltiple"  perfectamente  adecuada.

Compruebe  si  una  lista  contiene  un  valor  utilizando  la  palabra  clave  in :

En  [55]:  'enano'  en  b_list
Salida[55]:  Verdadero

La  palabra  clave  not  puede  usarse  para  negar  en:

En  [56]:  'enano'  no  en  b_list
Salida[56]:  Falso

Verificar  si  una  lista  contiene  un  valor  es  mucho  más  lento  que  hacerlo  con  dictados  y  conjuntos  (que  se  presentarán  
en  breve),  ya  que  Python  realiza  un  escaneo  lineal  a  través  de  los  valores  de  la  lista,  mientras  que  puede  verificar  
los  demás  (basado  en  tablas  hash)  en  tiempo  constante.

Concatenar  y  combinar  listas
Similar  a  las  tuplas,  agregar  dos  listas  junto  con  +  las  concatena:

En  [57]:  [4,  Ninguno,  'foo']  +  [7,  8,  (2,  3)]
Fuera[57]:  [4,  Ninguno,  'foo',  7,  8,  (2,  3)]

Si  ya  tiene  una  lista  definida,  puede  agregarle  varios  elementos  utilizando  el  método  de  extensión :

En  [58]:  x  =  [4,  Ninguno,  'foo']

En  [59]:  x.extender([7,  8,  (2,  3)])

En  [60]:  x
Fuera[60]:  [4,  Ninguno,  'foo',  7,  8,  (2,  3)]

Tenga  en  cuenta  que  la  concatenación  de  listas  por  adición  es  una  operación  comparativamente  costosa,  ya  que  
se  debe  crear  una  nueva  lista  y  copiar  los  objetos.  Usualmente  es  preferible  usar  extender  para  agregar  elementos  
a  una  lista  existente,  especialmente  si  está  creando  una  lista  grande.  De  este  modo,

56  |  Capítulo  3:  Estructuras  de  datos,  funciones  y  archivos  integrados
Machine Translated by Google

todo  =  []  para  
fragmento  en  list_of_lists:
todo.extender(trozo)

es  más  rápido  que  la  alternativa  concatenativa:

todo  =  []  para  
fragmento  en  list_of_lists:  todo  =  todo  
+  fragmento

Clasificación

Puede  ordenar  una  lista  en  el  lugar  (sin  crear  un  nuevo  objeto)  llamando  a  su  función  de  ordenación :

En  [61]:  a  =  [7,  2,  5,  1,  3]

En  [62]:  a.sort()

En  [63]:  un
Salida[63]:  [1,  2,  3,  5,  7]

sort  tiene  algunas  opciones  que  ocasionalmente  serán  útiles.  Una  es  la  capacidad  de  pasar  una  clave  
de  clasificación  secundaria,  es  decir,  una  función  que  produce  un  valor  para  usar  para  clasificar  los  
objetos.  Por  ejemplo,  podríamos  ordenar  una  colección  de  cadenas  por  sus  longitudes:

En  [64]:  b  =  ['sierra',  'pequeño',  'Él',  'zorros',  'seis']

En  [65]:  b.sort(key=len)

En  [66]:  b
Out[66]:  ['Él',  'vio',  'seis',  'pequeño',  'zorros']

Pronto  veremos  la  función  sorted ,  que  puede  producir  una  copia  ordenada  de  una  secuencia  general.

Búsqueda  binaria  y  mantenimiento  de  una  

lista  ordenada  El  módulo  bisect  incorporado  implementa  la  búsqueda  binaria  y  la  inserción  en  una  
lista  ordenada.  bisect.bisect  encuentra  la  ubicación  donde  se  debe  insertar  un  elemento  para  
mantenerlo  ordenado,  mientras  que  bisect.insort  en  realidad  inserta  el  elemento  en  esa  ubicación:

En  [67]:  importar  bisecar

En  [68]:  c  =  [1,  2,  2,  2,  3,  4,  7]

En  [69]:  bisect.bisect(c,  2)
Salida[69]:  4

En  [70]:  bisect.bisect(c,  5)
Salida[70]:  6

3.1  Estructuras  de  datos  y  secuencias  |  57
Machine Translated by Google

En  [71]:  bisect.insort(c,  6)

En  [72]:  c
Salida[72]:  [1,  2,  2,  2,  3,  4,  6,  7]

Las  funciones  del  módulo  Bisect  no  verifican  si  la  lista  está  ordenada,  ya  
que  hacerlo  sería  computacionalmente  costoso.  Por  lo  tanto,  usarlos  con  
una  lista  desordenada  tendrá  éxito  sin  errores,  pero  puede  conducir  a  
resultados  incorrectos.

rebanar

Puede  seleccionar  secciones  de  la  mayoría  de  los  tipos  de  secuencia  utilizando  la  notación  de  división,  que  
en  su  forma  básica  consiste  en  iniciar:  detener  pasado  al  operador  de  indexación  []:

En  [73]:  secuencia  =  [7,  2,  3,  7,  5,  6,  0,  1]

En  [74]:  sec[1:5]
Salida[74]:  [2,  3,  7,  5]

Los  cortes  también  se  pueden  asignar  con  una  secuencia:

En  [75]:  sec[3:4]  =  [6,  3]

En  [76]:  siguiente
Salida[76]:  [7,  2,  3,  6,  3,  5,  6,  0,  1]

Si  bien  se  incluye  el  elemento  en  el  índice  de  inicio ,  el  índice  de  finalización  no  se  incluye,  por  lo  que  el  
número  de  elementos  en  el  resultado  es  detener  ­  iniciar.

Se  puede  omitir  el  inicio  o  la  parada ,  en  cuyo  caso  por  defecto  son  el  inicio  de  la  secuencia  y  el  final  de  la  
secuencia,  respectivamente:

En  [77]:  sec[:5]
Salida[77]:  [7,  2,  3,  6,  3]

En  [78]:  sec[3:]
Salida[78]:  [6,  3,  5,  6,  0,  1]

Los  índices  negativos  dividen  la  secuencia  en  relación  con  el  final:

En  [79]:  sec[­4:]
Salida[79]:  [5,  6,  0,  1]

En  [80]:  sec[­6:­2]
Salida[80]:  [6,  3,  5,  6]

Se  necesita  un  poco  de  tiempo  para  acostumbrarse  a  la  semántica  de  corte,  especialmente  si  viene  de  R  o  
MATLAB.  Consulte  la  Figura  3­1  para  ver  una  ilustración  útil  de  dividir  con  enteros  positivos  y  negativos.  En  
la  figura,  los  índices  se  muestran  en  los  "bordes  de  los  bins"  para  ayudar  a  mostrar  dónde  comienzan  y  
terminan  las  selecciones  de  sectores  usando  índices  positivos  o  negativos.

58  |  Capítulo  3:  Estructuras  de  datos,  funciones  y  archivos  integrados
Machine Translated by Google

También  se  puede  usar  un  paso  después  de  dos  puntos  para,  por  ejemplo,  tomar  cualquier  otro  elemento:

En  [81]:  sec[::2]
Salida[81]:  [7,  3,  3,  6,  1]

Un  uso  inteligente  de  esto  es  pasar  ­1,  que  tiene  el  efecto  útil  de  invertir  una  lista  o  tupla:

En  [82]:  sec[::­1]
Salida[82]:  [1,  0,  6,  5,  3,  6,  3,  2,  7]

Figura  3­1.  Ilustración  de  las  convenciones  de  corte  de  Python

Funciones  de  secuencia  incorporadas  Python  

tiene  un  puñado  de  funciones  de  secuencia  útiles  con  las  que  debe  familiarizarse  y  usar  en  cualquier  oportunidad.

enumerar
Cuando  se  itera  sobre  una  secuencia,  es  común  querer  realizar  un  seguimiento  del  índice  del  elemento  actual.  Un  
enfoque  de  hágalo  usted  mismo  se  vería  así:

i  =  0  
para  el  valor  en  la  colección:
#  hacer  algo  con  valor
yo  +=  1

Dado  que  esto  es  tan  común,  Python  tiene  una  función  integrada,  enumerar,  que  devuelve  una  secuencia  de  (i,  valor)  
tuplas:

para  i,  valor  en  enumerar  (colección):  #  hacer  algo  con  
valor

Cuando  está  indexando  datos,  un  patrón  útil  que  usa  enumerate  es  calcular  un  dictado  que  asigna  los  valores  de  una  
secuencia  (que  se  supone  que  son  únicos)  a  sus  ubicaciones  en  la  secuencia:

En  [83]:  alguna_lista  =  ['foo',  'bar',  'baz']

En  [84]:  mapeo  =  {}

3.1  Estructuras  de  datos  y  secuencias  |  59
Machine Translated by Google

En  [85]:  for  i,  v  in  enumerate(some_list):  mapping[v]  
.....: =  i

En  [86]:  mapeo
Salida[86]:  {'barra':  1,  'baz':  2,  'foo':  0}

ordenado

La  función  sorted  devuelve  una  nueva  lista  ordenada  de  los  elementos  de  cualquier  secuencia:

En  [87]:  ordenado  ([7,  1,  2,  6,  0,  3,  2])
Salida[87]:  [0,  1,  2,  2,  3,  6,  7]

En  [88]:  sorted('  carrera  de  caballos')
Fuera[88]:  ['  ',  'a',  'c',  'e',  'e',  'h',  'o',  'r',  'r',  's']

La  función  ordenada  acepta  los  mismos  argumentos  que  el  método  de  clasificación  en  las  listas.

zip  

zip  “empareja”  los  elementos  de  una  serie  de  listas,  tuplas  u  otras  secuencias  para  crear  una  lista  de  tuplas:

En  [89]:  seq1  =  ['foo',  'bar',  'baz']

En  [90]:  seq2  =  ['uno',  'dos',  'tres']

En  [91]:  comprimido  =  zip(seq1,  seq2)

En  [92]:  lista  (comprimido)
Fuera[92]:  [('foo',  'uno'),  ('bar',  'dos'),  ('baz',  'tres')]

zip  puede  tomar  un  número  arbitrario  de  secuencias,  y  el  número  de  elementos  que  produce  está  
determinado  por  la  secuencia  más  corta:

En  [93]:  seq3  =  [Falso,  Verdadero]

En  [94]:  list(zip(seq1,  seq2,  seq3))
Fuera[94]:  [('foo',  'uno',  Falso),  ('bar',  'dos',  Verdadero)]

Un  uso  muy  común  de  zip  es  iterar  simultáneamente  sobre  múltiples  secuencias,  posiblemente  también  
combinado  con  enumerar:

En  [95]:  for  i,  (a,  b)  in  enumerate(zip(seq1,  seq2)):  print('{0}:  {1},  
.....: {2}'.format(i,  a,  b))
.....:
0:  foo,  uno  1:  
bar,  dos  2:  
baz,  tres

60  |  Capítulo  3:  Estructuras  de  datos,  funciones  y  archivos  integrados
Machine Translated by Google

Dada  una  secuencia  "comprimida",  se  puede  aplicar  zip  de  una  manera  inteligente  para  "descomprimir"  
la  secuencia.  Otra  forma  de  pensar  en  esto  es  convertir  una  lista  de  filas  en  una  lista  de  columnas.  La  
sintaxis,  que  parece  un  poco  mágica,  es:

En  [96]:  lanzadores  =  [('Nolan',  'Ryan'),  ('Roger',  'Clemens'),
.....: ('Schilling',  'Curt')]

En  [97]:  nombres,  apellidos  =  zip(*lanzadores)

En  [98]:  nombres
Fuera[98]:  ('Nolan',  'Roger',  'Schilling')

En  [99]:  apellidos
Fuera[99]:  ('Ryan',  'Clemens',  'Curt')

invertido

invertido  itera  sobre  los  elementos  de  una  secuencia  en  orden  inverso:

En  [100]:  lista  (invertida  (rango  (10)))
Salida[100]:  [9,  8,  7,  6,  5,  4,  3,  2,  1,  0]

Tenga  en  cuenta  que  invertido  es  un  generador  (que  se  analizará  con  más  detalle  más  adelante),  por  lo  
que  no  crea  la  secuencia  invertida  hasta  que  se  materializa  (por  ejemplo,  con  una  lista  o  un  bucle  for ).

dictar

dict  es  probablemente  la  estructura  de  datos  integrada  de  Python  más  importante.  Un  nombre  más  
común  para  él  es  mapa  hash  o  matriz  asociativa.  Es  una  colección  de  tamaño  flexible  de  pares  clave­
valor,  donde  clave  y  valor  son  objetos  de  Python.  Un  enfoque  para  crear  uno  es  usar  llaves  {}  y  dos  
puntos  para  separar  claves  y  valores:

En  [101]:  vacío_dict  =  {}

En  [102]:  d1  =  {'a' :  'algún  valor',  'b' :  [1,  2,  3,  4]}

En  [103]:  d1
Salida[103]:  {'a':  'algún  valor',  'b':  [1,  2,  3,  4]}

Puede  acceder,  insertar  o  configurar  elementos  usando  la  misma  sintaxis  que  para  acceder  a  elementos  
de  una  lista  o  tupla:

En  [104]:  d1[7]  =  'un  entero'

En  [105]:  d1
Out[105]:  {'a':  'algún  valor',  'b':  [1,  2,  3,  4],  7:  'un  número  entero'}

En  [106]:  d1['b']
Salida[106]:  [1,  2,  3,  4]

3.1  Estructuras  de  datos  y  secuencias  |  61
Machine Translated by Google

Puede  verificar  si  un  dictado  contiene  una  clave  utilizando  la  misma  sintaxis  utilizada  para  
verificar  si  una  lista  o  tupla  contiene  un  valor:

En  [107]:  'b'  en  d1
Fuera[107]:  Verdadero

Puede  eliminar  valores  utilizando  la  palabra  clave  del  o  el  método  pop  (que  simultáneamente  
devuelve  el  valor  y  elimina  la  clave):

En  [108]:  d1[5]  =  'algún  valor'

En  [109]:  d1
Out[109]:  
{'a':  'algún  valor',  'b':  [1,  
2,  3,  4],  7:  'un  entero',  
5:  'algún  valor'}

En  [110]:  d1['dummy']  =  'otro  valor'

En  [111]:  d1
Out[111]:  
{'a':  'algún  valor',  'b':  [1,  
2,  3,  4],  7:  'un  entero',  
5:  'algún  valor',  
'ficticio':  'otro  valor  
'}

En  [112]:  del  d1[5]

En  [113]:  d1
Out[113]:  
{'a':  'algún  valor',  'b':  [1,  
2,  3,  4],  7:  'un  entero',  
'ficticio':  'otro  valor'}

En  [114]:  ret  =  d1.pop('dummy')

En  [115]:  ret
Out[115]:  'otro  valor'

En  [116]:  d1
Out[116]:  {'a':  'algún  valor',  'b':  [1,  2,  3,  4],  7:  'un  número  entero'}

El  método  de  claves  y  valores  le  brinda  iteradores  de  las  claves  y  valores  del  dict,  
respectivamente.  Si  bien  los  pares  clave­valor  no  están  en  ningún  orden  en  particular,  estas  
funciones  generan  las  claves  y  los  valores  en  el  mismo  orden:

En  [117]:  lista  (d1.keys())
Salida[117]:  ['a',  'b',  7]

62  |  Capítulo  3:  Estructuras  de  datos,  funciones  y  archivos  integrados
Machine Translated by Google

En  [118]:  lista(d1.valores())
Out[118]:  ['algún  valor',  [1,  2,  3,  4],  'un  número  entero']

Puede  fusionar  un  dict  en  otro  usando  el  método  de  actualización :

En  [119]:  d1.update({'b' :  'foo',  'c' :  12})

En  [120]:  d1
Out[120]:  {'a':  'algún  valor',  'b':  'foo',  7:  'un  entero',  'c':  12}

El  método  de  actualización  cambia  los  dictados  en  el  lugar,  por  lo  que  cualquier  clave  existente  en  los  datos  pasados  
para  actualizar  tendrá  sus  valores  antiguos  descartados.

Creación  de  dictados  a  partir  de  

secuencias  En  ocasiones,  es  común  terminar  con  dos  secuencias  que  desea  emparejar  por  elementos  en  un  
dictado.  Como  primer  corte,  puede  escribir  un  código  como  este:

mapeo  =  {}  
para  clave,  valor  en  zip  (key_list,  value_list):
mapeo[clave]  =  valor

Dado  que  un  dict  es  esencialmente  una  colección  de  2  tuplas,  la  función  dict  acepta  una  lista  de  2  tuplas:

En  [121]:  mapeo  =  dict(zip(rango(5),  invertido(rango(5))))

En  [122]:  mapeo
Salida[122]:  {0:  4,  1:  3,  2:  2,  3:  1,  4:  0}

Más  adelante  hablaremos  sobre  las  comprensiones  de  dictados,  otra  forma  elegante  de  construir  dictados.

Valores  predeterminados

Es  muy  común  tener  una  lógica  como:

if  clave  en  some_dict:  
valor  =  some_dict[key]  
else:  
value  =  default_value

Por  lo  tanto,  los  métodos  dict  get  y  pop  pueden  tomar  un  valor  predeterminado  para  devolver,  de  modo  que  el  

bloque  if­else  anterior  se  puede  escribir  simplemente  como:

valor  =  some_dict.get(clave,  valor_predeterminado)

get  por  defecto  devolverá  None  si  la  clave  no  está  presente,  mientras  que  pop  generará  una  excepción.  Con  la  
configuración  de  valores,  un  caso  común  es  que  los  valores  en  un  dict  sean  otras  colecciones,  como  listas.  Por  
ejemplo,  podría  imaginar  clasificar  una  lista  de  palabras  por  sus  primeras  letras  como  un  dictado  de  listas:

En  [123]:  palabras  =  ['manzana',  'murciélago',  'barra',  'átomo',  'libro']

En  [124]:  por_letra  =  {}

3.1  Estructuras  de  datos  y  secuencias  |  63
Machine Translated by Google

En  [125]:  para  palabra  en  palabras:  
.....: letra  =  palabra[0]  si  
.....: letra  no  en  by_letter:
.....: by_letter[letra]  =  [palabra]  else:  
.....:
.....: by_letter[letra].append(palabra)
.....:

En  [126]:  por_letra
Salida[126]:  {'a':  ['manzana',  'átomo'],  'b':  ['murciélago',  'barra',  'libro']}

El  método  setdefault  dict  es  precisamente  para  este  propósito.  El  bucle  for  anterior  se  puede  
reescribir  como:

para  palabra  en  
palabras:  letra  =  
palabra[0]  by_letter.setdefault(letra,  []).append(palabra)

El  módulo  de  colecciones  incorporado  tiene  una  clase  útil,  defaultdict,  que  lo  hace  aún  más  
fácil.  Para  crear  uno,  pasa  un  tipo  o  función  para  generar  el  valor  predeterminado  para  cada  
ranura  en  el  dict:

from  collections  import  defaultdict  by_letter  
=  defaultdict(list)  for  word  in  words:

by_letter[palabra[0]].append(palabra)

Tipos  de  claves  de  

dictado  válidos  Si  bien  los  valores  de  un  dictado  pueden  ser  cualquier  objeto  de  Python,  las  
claves  generalmente  deben  ser  objetos  inmutables  como  tipos  escalares  (int,  float,  string)  o  
tuplas  (todos  los  objetos  en  la  tupla  también  deben  ser  inmutables) .  El  término  técnico  aquí  es  
hashability.  Puede  verificar  si  un  objeto  es  hashable  (se  puede  usar  como  clave  en  un  dict)  con  
la  función  hash :

En  [127]:  hash('cadena')
Salida[127]:  5023931463650008331

En  [128]:  hash((1,  2,  (2,  3)))
Salida[128]:  1097636502276347782

En  [129]:  hash((1,  2,  [2,  3]))  #  falla  porque  las  listas  son  mutables
­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­  ­­­­­­­­­­­­­­­­­­­­­­­­­

TypeError   Rastreo  (última  llamada  más  reciente)
<ipython­input­129­800cd14ba8be>  in  <module>()  ­­­­>  1  
hash((1,  2,  [2,  3]))  #  falla  porque  las  listas  son  mutables
TypeError:  tipo  no  modificable :  'lista'

64  |  Capítulo  3:  Estructuras  de  datos,  funciones  y  archivos  integrados
Machine Translated by Google

Para  usar  una  lista  como  clave,  una  opción  es  convertirla  en  una  tupla,  que  se  puede  codificar  siempre  que  sus  elementos  también  lo  

hagan:

En  [130]:  d  =  {}

En  [131]:  d[tupla([1,  2,  3])]  =  5

En  [132]:  d
Salida[132]:  {(1,  2,  3):  5}

colocar

Un  conjunto  es  una  colección  desordenada  de  elementos  únicos.  Puede  pensar  en  ellos  como  dictados,  pero  solo  claves,  sin  valores.  

Un  conjunto  se  puede  crear  de  dos  maneras:  mediante  la  función  de  conjunto  o  mediante  un  conjunto  literal  con  llaves:

En  [133]:  conjunto  ([2,  2,  2,  1,  3,  3])
Salida[133]:  {1,  2,  3}

En  [134]:  {2,  2,  2,  1,  3,  3}
Salida[134]:  {1,  2,  3}

Los  conjuntos  admiten  operaciones  de  conjuntos  matemáticos  como  unión,  intersección,  diferencia  y  diferencia  simétrica.  Considere  

estos  dos  conjuntos  de  ejemplo:

En  [135]:  a  =  {1,  2,  3,  4,  5}

En  [136]:  b  =  {3,  4,  5,  6,  7,  8}

La  unión  de  estos  dos  conjuntos  es  el  conjunto  de  elementos  distintos  que  se  encuentran  en  cualquiera  de  los  conjuntos.  Esto  se  

puede  calcular  con  el  método  de  unión  o  con  el  método  |  operador  binario:

En  [137]:  a.union(b)
Salida[137]:  {1,  2,  3,  4,  5,  6,  7,  8}

En  [138]:  un  |  b
Salida[138]:  {1,  2,  3,  4,  5,  6,  7,  8}

La  intersección  contiene  los  elementos  que  ocurren  en  ambos  conjuntos.  Se  puede  utilizar  el  operador  &  o  el  método  de  intersección :

En  [139]:  a.intersección(b)
Salida[139]:  {3,  4,  5}

En  [140]:  a  &  b
Salida[140]:  {3,  4,  5}

Consulte  la  Tabla  3­1  para  obtener  una  lista  de  los  métodos  de  ajuste  más  utilizados.

3.1  Estructuras  de  datos  y  secuencias  |  sesenta  y  cinco
Machine Translated by Google

Tabla  3­1.  Operaciones  de  conjuntos  de  Python

Función Alternativa Descripción

sintaxis

a.añadir(x) N /  A Añadir  el  elemento  x  al  conjunto  a

una  limpieza() N /  A Restablezca  el  conjunto  a  a  un  estado  vacío,  descartando  todos


sus  elementos

a.quitar(x) N /  A Eliminar  el  elemento  x  del  conjunto  a

Un  estallido() N /  A Eliminar  un  elemento  arbitrario  del  conjunto  a,  elevando

KeyError  si  el  conjunto  está  vacío

a.unión(b) un  |  b Todos  los  elementos  únicos  en  a  y  b

a.actualizar(b) un  |=  segundo Establecer  el  contenido  de  a  para  que  sea  la  unión  de  los

elementos  en  a  y  b

a.intersección(b) a  &  b Todos  los  elementos  tanto  en  a  como  en  b

a.intersection_update(b) un  &  =  segundo Establezca  el  contenido  de  a  para  que  sea  la  intersección  de  la

elementos  en  a  y  b

a.diferencia(b) un­b Los  elementos  de  a  que  no  están  en  b

a.difference_update(b) un  ­  =  segundo Establecer  a  a  los  elementos  en  a  que  no  están  en  b

a.diferencia_simétrica(b) a ^  segundo Todos  los  elementos  en  a  o  b  pero  no  en  ambos

a.diferencia_simétrica_actualización(b)  a  ^=  b Establezca  a  para  que  contenga  los  elementos  en  a  o  b  pero

no  ambos

a.es  un  subconjunto(b) N /  A Verdadero  si  los  elementos  de  a  están  todos  contenidos  en  b

a.essuperconjunto(b) N /  A Verdadero  si  los  elementos  de  b  están  todos  contenidos  en  a

a.es  disjunto(b) N /  A Verdadero  si  a  y  b  no  tienen  elementos  en  común

Todas  las  operaciones  de  conjuntos  lógicos  tienen  contrapartes  en  el  lugar,  que  le  permiten
reemplace  el  contenido  del  conjunto  en  el  lado  izquierdo  de  la  operación  con  el  resultado.  Para

conjuntos  muy  grandes,  esto  puede  ser  más  eficiente:

En  [141]:  c  =  a.copia()

En  [142]:  c  |=  b

En  [143]:  c
Salida[143]:  {1,  2,  3,  4,  5,  6,  7,  8}

En  [144]:  d  =  a.copia()

En  [145]:  d  &=  b

En  [146]:  d
Salida[146]:  {3,  4,  5}

Al  igual  que  los  dictados,  los  elementos  establecidos  generalmente  deben  ser  inmutables.  Para  tener  elementos  tipo  lista,  usted
debe  convertirlo  en  una  tupla:

66  |  Capítulo  3:  Estructuras  de  datos,  funciones  y  archivos  integrados
Machine Translated by Google

En  [147]:  mis_datos  =  [1,  2,  3,  4]

En  [148]:  mi_conjunto  =  {tupla(mis_datos)}

En  [149]:  mi_conjunto
Salida[149]:  {(1,  2,  3,  4)}

También  puede  verificar  si  un  conjunto  es  un  subconjunto  de  (está  contenido  en)  o  un  superconjunto  de  (contiene  
todos  los  elementos  de)  otro  conjunto:

En  [150]:  un_conjunto  =  {1,  2,  3,  4,  5}

En  [151]:  {1,  2,  3}.issubset(a_set)
Fuera[151]:  Verdadero

En  [152]:  a_set.issuperset({1,  2,  3})
Salida[152]:  Verdadero

Los  conjuntos  son  iguales  si  y  solo  si  sus  contenidos  son  iguales:

En  [153]:  {1,  2,  3}  ==  {3,  2,  1}
Salida[153]:  Verdadero

Comprensiones  de  listas,  conjuntos  y  dictados  Las  

comprensiones  de  listas  son  una  de  las  características  más  queridas  del  lenguaje  Python.  Le  permiten  formar  de  
manera  concisa  una  nueva  lista  filtrando  los  elementos  de  una  colección,  transformando  los  elementos  que  pasan  el  
filtro  en  una  expresión  concisa.  Toman  la  forma  básica:

[expr  para  val  en  colección  si  condición]

Esto  es  equivalente  al  siguiente  bucle  for :

resultado  =  []  
para  val  en  colección:  si  
condición:
resultado.append(expr)

La  condición  de  filtro  se  puede  omitir,  dejando  solo  la  expresión.  Por  ejemplo,  dada  una  lista  de  cadenas,  podríamos  
filtrar  las  cadenas  con  una  longitud  de  2  o  menos  y  también  convertirlas  a  mayúsculas  de  esta  manera:

En  [154]:  strings  =  ['a',  'as',  'bat',  'car',  'dove',  'python']

En  [155]:  [x.upper()  para  x  en  cadenas  si  len(x)  >  2]
Out[155]:  ['BAT',  'CAR',  'DOVE',  'PYTHON']

Las  comprensiones  de  conjuntos  y  dictados  son  una  extensión  natural,  produciendo  conjuntos  y  dictados  de  una  
manera  idiomáticamente  similar  en  lugar  de  listas.  Una  comprensión  dictada  se  ve  así:

dict_comp  =  {key­expr :  value­expr  para  el  valor  en  la  colección
si  condición}

3.1  Estructuras  de  datos  y  secuencias  |  67
Machine Translated by Google

Una  comprensión  de  conjunto  se  parece  a  la  comprensión  de  lista  equivalente,  excepto  que  lleva  llaves  en  lugar  
de  corchetes:

set_comp  =  {expr  para  valor  en  colección  si  condición}

Al  igual  que  las  comprensiones  de  listas,  las  comprensiones  de  conjuntos  y  dictados  son  en  su  mayoría  
conveniencias,  pero  de  manera  similar  pueden  hacer  que  el  código  sea  más  fácil  de  escribir  y  leer.  Considere  la  
lista  de  cadenas  de  antes.  Supongamos  que  queremos  un  conjunto  que  contenga  solo  las  longitudes  de  las  
cadenas  contenidas  en  la  colección;  Podríamos  calcular  esto  fácilmente  usando  una  comprensión  establecida:

En  [156]:  longitudes_únicas  =  {len(x)  para  x  en  cadenas}

En  [157]:  longitudes_únicas
Salida[157]:  {1,  2,  3,  4,  6}

También  podríamos  expresar  esto  de  manera  más  funcional  utilizando  la  función  de  mapa ,  que  se  presenta  en  
breve:

En  [158]:  set(mapa(longitud,  cadenas))
Salida[158]:  {1,  2,  3,  4,  6}

Como  ejemplo  simple  de  comprensión  de  dictados,  podríamos  crear  un  mapa  de  búsqueda  de  estas  cadenas  
en  sus  ubicaciones  en  la  lista:

En  [159]:  loc_mapping  =  {val :  índice  por  índice,  val  en  enumerar  (cadenas)}

En  [160]:  loc_mapping
Salida[160]:  {'a':  0,  'como':  1,  'murciélago':  2,  'coche':  3,  'paloma':  4,  'pitón':  5}

Comprensiones  de  listas  anidadas

Supongamos  que  tenemos  una  lista  de  listas  que  contienen  algunos  nombres  en  inglés  y  español:

En  [161]:  all_data  =  [['John',  'Emily',  'Michael',  'Mary',  'Steven'],
.....: ['María',  'Juan',  'Javier',  'Natalia',  'Pilar']]

Es  posible  que  haya  obtenido  estos  nombres  de  un  par  de  archivos  y  haya  decidido  organizarlos  por  idioma.  
Ahora,  supongamos  que  queremos  obtener  una  sola  lista  que  contenga  todos  los  nombres  con  dos  o  más  e  en  
ellos.  Ciertamente  podríamos  hacer  esto  con  un  bucle  for  simple :

names_of_interest  =  []  para  
nombres  en  all_data:
suficientes_es  =  [nombre  por  nombre  en  nombres  si  nombre.cuenta('e')  >=  
2]  nombres_de_interes.extender(suficientes)

En  realidad,  puede  envolver  toda  esta  operación  en  una  sola  comprensión  de  lista  anidada,  que  se  verá  así:

En  [162]:  resultado  =  [nombre  para  nombres  en  todos  los  datos  para  nombre  en  
.....: nombres  si  nombre.cuenta('e')  >=  2]

En  [163]:  resultado
Fuera[163]:  ['Steven']

68  |  Capítulo  3:  Estructuras  de  datos,  funciones  y  archivos  integrados
Machine Translated by Google

Al  principio,  las  comprensiones  de  listas  anidadas  son  un  poco  difíciles  de  entender.  Las  partes  for  de  
la  comprensión  de  la  lista  se  organizan  de  acuerdo  con  el  orden  de  anidamiento,  y  cualquier  condición  
de  filtro  se  coloca  al  final  como  antes.  Aquí  hay  otro  ejemplo  en  el  que  "aplanamos"  una  lista  de  tuplas  
de  enteros  en  una  lista  simple  de  enteros:

En  [164]:  algunas_tuplas  =  [(1,  2,  3),  (4,  5,  6),  (7,  8,  9)]

En  [165]:  flattened  =  [x  para  tup  en  some_tuples  para  x  en  tup]

En  [166]:  aplanado
Salida[166]:  [1,  2,  3,  4,  5,  6,  7,  8,  9]

Tenga  en  cuenta  que  el  orden  de  las  expresiones  for  sería  el  mismo  si  escribiera  un  bucle  for  anidado  
en  lugar  de  una  lista  de  comprensión:

aplanado  =  []

for  tup  en  some_tuples:  for  
x  in  tup:  
flattened.append(x)

Puede  tener  arbitrariamente  muchos  niveles  de  anidamiento,  aunque  si  tiene  más  de  dos  o  tres  
niveles  de  anidamiento,  probablemente  debería  comenzar  a  preguntarse  si  esto  tiene  sentido  desde  
el  punto  de  vista  de  la  legibilidad  del  código.  Es  importante  distinguir  la  sintaxis  que  se  acaba  de  
mostrar  de  una  comprensión  de  lista  dentro  de  una  comprensión  de  lista,  que  también  es  perfectamente  válida:

En  [167]:  [[x  for  x  in  tup]  for  tup  in  some_tuples]
Fuera[167]:  [[1,  2,  3],  [4,  5,  6],  [7,  8,  9]]

Esto  produce  una  lista  de  listas,  en  lugar  de  una  lista  plana  de  todos  los  elementos  internos.

3.2  Funciones
Las  funciones  son  el  método  principal  y  más  importante  de  organización  y  reutilización  de  código  en  
Python.  Como  regla  general,  si  prevé  que  necesitará  repetir  el  mismo  código  o  uno  muy  similar  más  
de  una  vez,  puede  valer  la  pena  escribir  una  función  reutilizable.  Las  funciones  también  pueden  
ayudar  a  que  su  código  sea  más  legible  dando  un  nombre  a  un  grupo  de  sentencias  de  Python.

Las  funciones  se  declaran  con  la  palabra  clave  def  y  se  devuelven  con  la  palabra  clave  return :

def  mi_funcion(x,  y,  z=1.5):
si  z  >  1:
devuelve   *  (x  +  y)
z  
sino:  devuelve  z /  (x  +  y)

3.2  Funciones  |  69
Machine Translated by Google

No  hay  problema  con  tener  varias  declaraciones  de  devolución .  Si  Python  llega  al  final  de  una  función  
sin  encontrar  una  declaración  de  retorno ,  se  devuelve  Ninguno  automáticamente.

Cada  función  puede  tener  argumentos  posicionales  y  argumentos  de  palabras  clave.  Los  argumentos  de  
palabras  clave  se  usan  más  comúnmente  para  especificar  valores  predeterminados  o  argumentos  
opcionales.  En  la  función  anterior,  x  e  y  son  argumentos  posicionales  mientras  que  z  es  un  argumento  
de  palabra  clave.  Esto  significa  que  la  función  se  puede  llamar  de  cualquiera  de  estas  formas:

mi_funcion(5,  6,  z=0.7)  
mi_funcion(3.14,  7,  3.5)  
mi_funcion(10,  20)

La  principal  restricción  en  los  argumentos  de  función  es  que  los  argumentos  de  palabras  clave  deben  
seguir  a  los  argumentos  posicionales  (si  los  hay).  Puede  especificar  argumentos  de  palabras  clave  en  
cualquier  orden;  esto  lo  libera  de  tener  que  recordar  en  qué  orden  se  especificaron  los  argumentos  de  la  
función  y  solo  cuáles  son  sus  nombres.

También  es  posible  usar  palabras  clave  para  pasar  argumentos  posicionales.  En  el  
ejemplo  anterior,  también  podríamos  haber  escrito:

mi_funcion(x=5,  y=6,  z=7)  
mi_funcion(y=6,  x=5,  z=7)
En  algunos  casos,  esto  puede  ayudar  con  la  legibilidad.

Espacios  de  nombres,  ámbito  y  funciones  locales  Las  

funciones  pueden  acceder  a  variables  en  dos  ámbitos  diferentes:  global  y  local.  Un  nombre  alternativo  y  
más  descriptivo  que  describe  un  ámbito  variable  en  Python  es  un  espacio  de  nombres.  Todas  las  
variables  que  se  asignan  dentro  de  una  función  de  forma  predeterminada  se  asignan  al  espacio  de  
nombres  local.  El  espacio  de  nombres  local  se  crea  cuando  se  llama  a  la  función  y  se  llena  inmediatamente  
con  los  argumentos  de  la  función.  Una  vez  finalizada  la  función,  el  espacio  de  nombres  local  se  destruye  
(con  algunas  excepciones  que  están  fuera  del  alcance  de  este  capítulo).  Considere  la  siguiente  función:

def  func():  a  
=  []  for  
i  in  range(5):  
a.append(i)

Cuando  se  llama  a  func() ,  se  crea  la  lista  vacía  a ,  se  agregan  cinco  elementos  y  luego  se  destruye  a  
cuando  la  función  sale.  Supongamos  que,  en  cambio,  hubiéramos  declarado  a  de  la  siguiente  manera:

a  =  []  
def  func():  
for  i  in  range(5):  
a.append(i)

70  |  Capítulo  3:  Estructuras  de  datos,  funciones  y  archivos  integrados
Machine Translated by Google

Es  posible  asignar  variables  fuera  del  alcance  de  la  función,  pero  esas  variables  deben  
declararse  como  globales  a  través  de  la  palabra  clave  global :

En  [168]:  a  =  Ninguno

En  [169]:  def  bind_a_variable():  global  
.....: aa  =  
.....: [] .....:  
bind_a_variable()
.....:

En  [170]:  imprimir(a)  []

Generalmente  desaconsejo  el  uso  de  la  palabra  clave  global .  Normalmente,  las  
variables  globales  se  utilizan  para  almacenar  algún  tipo  de  estado  en  un  sistema.  
Si  te  encuentras  usando  muchos  de  ellos,  puede  indicar  una  necesidad  de  
programación  orientada  a  objetos  (usando  clases).

Devolver  valores  múltiples  Cuando  

programé  por  primera  vez  en  Python  después  de  haber  programado  en  Java  y  C++,  una  de  
mis  funciones  favoritas  fue  la  capacidad  de  devolver  valores  múltiples  desde  una  función  con  
una  sintaxis  simple.  Aquí  hay  un  ejemplo:

definición  
f():  a  =  
5  b  =  6
c  =  7  
devuelve  a,  b,  c

a,  b,  c  =  f()

En  el  análisis  de  datos  y  otras  aplicaciones  científicas,  es  posible  que  se  encuentre  haciendo  
esto  con  frecuencia.  Lo  que  sucede  aquí  es  que  la  función  en  realidad  solo  devuelve  un  objeto,  
es  decir,  una  tupla,  que  luego  se  desempaqueta  en  las  variables  de  resultado.  En  el  ejemplo  
anterior,  podríamos  haber  hecho  esto  en  su  lugar:

valor_devuelto  =  f()

En  este  caso,  return_value  sería  una  tupla  de  3  con  las  tres  variables  devueltas.  Una  alternativa  
potencialmente  atractiva  para  devolver  múltiples  valores  como  antes  podría  ser  devolver  un  
dict  en  su  lugar:

definición  
f():  a  =  
5  b  =  6
c  =  7  
devuelve  {'a' :  a,  'b' :  b,  'c' :  c}

3.2  Funciones  |  71
Machine Translated by Google

Esta  técnica  alternativa  puede  ser  útil  según  lo  que  intente  hacer.

Las  funciones  son  objetos  

Dado  que  las  funciones  de  Python  son  objetos,  se  pueden  expresar  fácilmente  muchas  construcciones  
que  son  difíciles  de  hacer  en  otros  lenguajes.  Supongamos  que  estábamos  haciendo  una  limpieza  de  
datos  y  necesitábamos  aplicar  un  montón  de  transformaciones  a  la  siguiente  lista  de  cadenas:

En  [171]:  estados  =  ['  Alabama  ',  'Georgia!',  'Georgia',  'georgia',  'FlOrIda',  'south  carolina##',  'West  
.....: virginia?']

Cualquiera  que  haya  trabajado  alguna  vez  con  datos  de  encuestas  enviados  por  usuarios  ha  visto  
resultados  confusos  como  estos.  Deben  suceder  muchas  cosas  para  que  esta  lista  de  cadenas  sea  
uniforme  y  esté  lista  para  el  análisis:  eliminar  los  espacios  en  blanco,  eliminar  los  símbolos  de  
puntuación  y  estandarizar  con  mayúsculas  adecuadas.  Una  forma  de  hacer  esto  es  usar  métodos  de  
cadena  incorporados  junto  con  el  módulo  de  biblioteca  estándar  para  expresiones  regulares:

importar  re

def  clean_strings(cadenas):  
resultado  =  []  
for  valor  en  cadenas:  
valor  =  valor.strip()  valor  
=  re.sub('[!#?]',  '',  valor)  valor  =  valor.título()  
resultado.  agregar  (valor)  
resultado  devuelto

El  resultado  se  ve  así:

En  [173]:  clean_strings(estados)
Fuera[173]:
['Alabama',
'Georgia',
'Georgia',
'Georgia',
'Florida',
'Carolina  del  Sur',
'Virginia  del  Oeste']

Un  enfoque  alternativo  que  puede  resultarle  útil  es  hacer  una  lista  de  las  operaciones  que  desea  aplicar  
a  un  conjunto  particular  de  cadenas:

def  remove_puntuación(valor):
volver  re.sub('[!#?]',  '',  valor)

clean_ops  =  [str.strip,  remove_punctuation,  str.title]

def  clean_strings(cadenas,  operaciones):
resultado  =  []  
para  valor  en  cadenas:  
para  función  en  ops:

72  |  Capítulo  3:  Estructuras  de  datos,  funciones  y  archivos  integrados
Machine Translated by Google

valor  =  función(valor)  
resultado.agregar(valor)  
devolver  resultado

Luego  tenemos  lo  siguiente:

En  [175]:  clean_strings(estados,  clean_ops)
Fuera[175]:
['Alabama',
'Georgia',
'Georgia',
'Georgia',
'Florida',
'Carolina  del  Sur',
'Virginia  del  Oeste']

Un  patrón  más  funcional  como  este  le  permite  modificar  fácilmente  cómo  se  transforman  las  
cadenas  a  un  nivel  muy  alto.  La  función  clean_strings  ahora  también  es  más  reutilizable  y  
genérica.

Puede  usar  funciones  como  argumentos  para  otras  funciones,  como  la  función  de  mapa  
integrada ,  que  aplica  una  función  a  una  secuencia  de  algún  tipo:

En  [176]:  para  x  en  el  mapa  (remove_punctuation,  estados):
.....: imprimir  (x)
Alabama
Georgia
georgia  
georgia
Florida
Carolina  del  Sur
Virginia  del  Oeste

Funciones  anónimas  (Lambda)  Python  tiene  

soporte  para  las  llamadas  funciones  anónimas  o  lambda,  que  son  una  forma  de  escribir  
funciones  que  consisten  en  una  sola  declaración,  cuyo  resultado  es  el  valor  de  retorno.  Se  
definen  con  la  palabra  clave  lambda ,  que  no  tiene  otro  significado  que  "estamos  declarando  
una  función  anónima":

def  función_corto(x):
volver  x *  2

equiv_anon  =  lambda  x:  x *  2

Normalmente  me  refiero  a  estas  como  funciones  lambda  en  el  resto  del  libro.  Son  especialmente  
convenientes  en  el  análisis  de  datos  porque,  como  verá,  hay  muchos  casos  en  los  que  las  
funciones  de  transformación  de  datos  tomarán  funciones  como  argumentos.  A  menudo  es  
menos  tipeo  (y  más  claro)  pasar  una  función  lambda  en  lugar  de  escribir  una  declaración  de  
función  completa  o  incluso  asignar  la  función  lambda  a  una  variable  local.  Por  ejemplo,  
considere  este  ejemplo  tonto:

3.2  Funciones  |  73
Machine Translated by Google

def  aplicar_a_la_lista(alguna_lista,  f):
devuelve  [f(x)  por  x  en  alguna_lista]

enteros  =  [4,  0,  1,  5,  6]  
apply_to_list(ints,  lambda  x:  x  *  2)

También  podría  haber  escrito  [x  *  2  para  x  en  ints],  pero  aquí  pudimos  pasar  sucintamente  un  operador  
personalizado  a  la  función  apply_to_list .

Como  otro  ejemplo,  suponga  que  desea  ordenar  una  colección  de  cadenas  por  el  número  de  letras  distintas  
en  cada  cadena:

En  [177]:  cadenas  =  ['foo',  'tarjeta',  'bar',  'aaaa',  'abab']

Aquí  podríamos  pasar  una  función  lambda  al  método  de  clasificación  de  la  lista :

En  [178]:  strings.sort(key=lambda  x:  len(set(list(x))))

En  [179]:  cadenas
Salida[179]:  ['aaaa',  'foo',  'abab',  'bar',  'tarjeta']

Una  de  las  razones  por  las  que  las  funciones  lambda  se  denominan  funciones  anónimas  
ese   , es  que,  a  diferencia  de  las  funciones  declaradas  con  la  palabra  clave  def ,  la  función
objeto  en  sí  nunca  recibe  un  atributo  __name__  explícito.

Currying:  aplicación  de  argumentos  parciales  Currying  es  

una  jerga  informática  (llamada  así  por  el  matemático  Haskell  Curry)  que  significa  derivar  nuevas  funciones  
de  las  existentes  mediante  la  aplicación  de  argumentos  parciales.  Por  ejemplo,  supongamos  que  tenemos  
una  función  trivial  que  suma  dos  números:

def  suma_números(x,  y):  
devuelve  x  +  y

Usando  esta  función,  podríamos  derivar  una  nueva  función  de  una  variable,  suma_cinco,  que  suma  5  a  su  
argumento:

sumar_cinco  =  lambda  y:  sumar_números(5,  y)

Se  dice  que  el  segundo  argumento  de  add_numbers  está  currado.  No  hay  nada  muy  elegante  aquí,  ya  que  
todo  lo  que  hemos  hecho  realmente  es  definir  una  nueva  función  que  llama  a  una  función  existente.
El  módulo  integrado  de  funciones  puede  simplificar  este  proceso  utilizando  la  función  parcial :

de  functools  importar  parcial  
add_five  =  parcial  (add_numbers,  5)

74  |  Capítulo  3:  Estructuras  de  datos,  funciones  y  archivos  integrados
Machine Translated by Google

Generadores
Tener  una  forma  consistente  de  iterar  sobre  secuencias,  como  objetos  en  una  lista  o  líneas  en  un  archivo,  
es  una  característica  importante  de  Python.  Esto  se  logra  mediante  el  protocolo  iterador,  una  forma  genérica  
de  hacer  que  los  objetos  sean  iterables.  Por  ejemplo,  iterar  sobre  un  dict  produce  las  claves  de  dict:

En  [180]:  some_dict  =  {'a':  1,  'b':  2,  'c':  3}

En  [181]:  for  key  en  some_dict:  
.....: print(key)
a
b
C

Cuando  escribe  para  clave  en  some_dict,  el  intérprete  de  Python  primero  intenta  crear  un  iterador  a  partir  
de  some_dict:

En  [182]:  dict_iterator  =  iter(some_dict)

En  [183]:  dict_iterator
Salida[183]:  <dict_keyiterator  en  0x7fbbd5a9f908>

Un  iterador  es  cualquier  objeto  que  le  dará  objetos  al  intérprete  de  Python  cuando  se  usa  en  un  
contexto  como  un  bucle  for .  La  mayoría  de  los  métodos  que  esperan  una  lista  o  un  objeto  similar  
a  una  lista  también  aceptarán  cualquier  objeto  iterable.  Esto  incluye  métodos  integrados  como  min,  
max  y  sum,  y  constructores  de  tipos  como  list  y  tuple:

En  [184]:  lista  (dict_iterator)
Salida[184]:  ['a',  'b',  'c']

Un  generador  es  una  forma  concisa  de  construir  un  nuevo  objeto  iterable.  Mientras  que  las  funciones  
normales  se  ejecutan  y  devuelven  un  solo  resultado  a  la  vez,  los  generadores  devuelven  una  secuencia  de  
múltiples  resultados  de  forma  perezosa,  deteniéndose  después  de  cada  uno  hasta  que  se  solicita  el  
siguiente.  Para  crear  un  generador,  use  la  palabra  clave  yield  en  lugar  de  return  en  una  función:

def  squares(n=10):  
print('Generando  cuadrados  de  1  a  {0}'.format(n  **  2))  for  i  in  range(1,  
n  +  1):  yield  i  **  2

Cuando  realmente  llama  al  generador,  no  se  ejecuta  ningún  código  inmediatamente:

En  [186]:  gen  =  cuadrados()

En  [187]:  gen
Out[187]:  <  cuadrados  del  objeto  generador  en  0x7fbbd5ab4570>

No  es  hasta  que  solicita  elementos  del  generador  que  comienza  a  ejecutar  su  código:

3.2  Funciones  |  75
Machine Translated by Google

En  [188]:  para  x  en  gen:  
.....: print(x,  end='  ')
Generando  cuadrados  del  1  al  100  1  4  
9  16  25  36  49  64  81  100

Expresiones  

generadoras  Otra  forma  aún  más  concisa  de  hacer  un  generador  es  usando  una  expresión  
generadora.  Este  es  un  generador  análogo  a  listas,  dictados  y  conjuntos  de  comprensiones;  
para  crear  uno,  encierre  lo  que  de  otro  modo  sería  una  lista  de  comprensión  entre  paréntesis  
en  lugar  de  corchetes:

En  [189]:  gen  =  (x  **  2  para  x  en  el  rango  (100))

En  [190]:  gen
Salida[190]:  <  objeto  generador  <genexpr>  en  0x7fbbd5ab29e8>

Esto  es  completamente  equivalente  al  siguiente  generador  más  detallado:

def  _make_gen():  
for  x  in  range(100):  
rendimiento  x  
**  2  gen  =  _make_gen()

Las  expresiones  generadoras  se  pueden  usar  en  lugar  de  listas  por  comprensión  como  argumentos  de  
funciones  en  muchos  casos:

En  [191]:  suma  (x  **  2  para  x  en  el  rango  (100))
Salida[191]:  328350

En  [192]:  dict((i,  i  **2)  for  i  in  range(5))
Salida[192]:  {0:  0,  1:  1,  2:  4,  3:  9,  4:  16}

módulo  itertools

El  módulo  itertools  de  la  biblioteca  estándar  tiene  una  colección  de  generadores  para  muchos  algoritmos  
de  datos  comunes.  Por  ejemplo,  groupby  toma  cualquier  secuencia  y  una  función,  agrupando  elementos  
consecutivos  en  la  secuencia  por  el  valor  de  retorno  de  la  función.  Aquí  hay  un  ejemplo:

En  [193]:  importar  itertools

En  [194]:  primera_letra  =  lambda  x:  x[0]

En  [195]:  nombres  =  ['Alan',  'Adam',  'Wes',  'Will',  'Albert',  'Steven']

En  [196]:  para  letra,  nombres  en  itertools.groupby(nombres,  primera_letra):
.....: print(letra,  lista(nombres))  #  nombres  es  un  generador
A  ['Alan',  'Adán']
W  ['Wes',  'Voluntad']
A  ['Alberto']
S  ['Steven']

76  |  Capítulo  3:  Estructuras  de  datos,  funciones  y  archivos  integrados
Machine Translated by Google

Consulte  la  Tabla  3­2  para  obtener  una  lista  de  algunas  otras  funciones  de  itertools  que  con  frecuencia  he  
encontrado  útiles.  Puede  consultar  la  documentación  oficial  de  Python  para  obtener  más  información  sobre  
este  útil  módulo  de  utilidad  incorporado.

Tabla  3­2.  Algunas  funciones  útiles  de  itertools
Función Descripción

combinaciones(iterable,  k) Genera  una  secuencia  de  todas  las  k­tuplas  posibles  de  elementos  en  el  orden  
iterable,  ignorando  y  sin  reemplazo  (consulte  también  la  función  complementaria  
combinaciones_con_reemplazo)

permutaciones(iterable,  k) Genera  una  secuencia  de  todas  las  k­tuplas  posibles  de  elementos  en  el  orden  
iterable  respetando

groupby(iterable[,  keyfunc])  Genera  (clave,  sub­iterador)  para  cada  producto  clave  único(*iterables,  repeat=1)  Genera  

el  producto  cartesiano  de  los  iterables  de  entrada  como  tuplas,  similar  a  un  bucle  for  anidado

Manejo  de  errores  y  excepciones  El  manejo  

correcto  de  errores  o  excepciones  de  Python  es  una  parte  importante  de  la  construcción  de  programas  
robustos.  En  las  aplicaciones  de  análisis  de  datos,  muchas  funciones  solo  funcionan  en  ciertos  tipos  de  
entrada.  Como  ejemplo,  la  función  flotante  de  Python  es  capaz  de  convertir  una  cadena  en  un  número  de  
coma  flotante,  pero  falla  con  ValueError  en  entradas  incorrectas:

En  [197]:  flotante('1.2345')
Salida[197]:  1.2345

En  [198]:  float('algo')
­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­  ­­­­­­­­­­­­­­­­­­­­­­­­­

ValueError   Rastreo  (última  llamada  más  reciente)
<ipython­input­198­439904410854>  en  <módulo>()  ­­­­>  1  
float('algo')
ValueError:  no  se  pudo  convertir  la  cadena  en  flotante:  'algo'

Supongamos  que  queremos  una  versión  de  float  que  falle  correctamente  y  devuelva  el  argumento  de  
entrada.  Podemos  hacer  esto  escribiendo  una  función  que  incluya  la  llamada  a  flotar  en  un  bloque  try/  
except :

def  intent_float(x):  try:  return  

float(x)  excepto:

volver  x

El  código  en  la  parte  excepto  del  bloque  solo  se  ejecutará  si  float(x)  genera  una  excepción:

En  [200]:  intent_float('1.2345')
Salida[200]:  1.2345

3.2  Funciones  |  77
Machine Translated by Google

En  [201]:  intent_float('algo')
Fuera[201]:  'algo'

Puede  notar  que  float  puede  generar  excepciones  distintas  de  ValueError:

En  [202]:  flotante  ((1,  2))
­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­  ­­­­­­­­­­­­­­­­­­­­­­­­­

TypeError   Rastreo  (última  llamada  más  reciente)
<ipython­input­202­842079ebb635>  en  <módulo>()  ­­­­>  1  
float((1,  2))
TypeError:  el  argumento  float()  debe  ser  una  cadena  o  un  número,  no  una  'tupla'

Es  posible  que  desee  suprimir  solo  ValueError,  ya  que  un  TypeError  (la  entrada  no  era  una  cadena  o  un  valor  numérico)  
podría  indicar  un  error  legítimo  en  su  programa.  Para  hacer  eso,  escriba  el  tipo  de  excepción  después  de  excepto:

def  intent_float(x):  try:  return  

float(x)  excepto  
ValueError:  return  x

Tenemos  entonces:

En  [204]:  intent_float((1,  2))
­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­  ­­­­­­­­­­­­­­­­­­­­­­­­­

TypeError   Rastreo  (última  llamada  más  reciente)
<ipython­input­204­9bdfd730cead>  en  <módulo>()  ­­­­>  1  
intent_float((1,  2))  <ipython­
input­203­3e06b8379b6b>  en  intent_float(x)
1  def  intent_float(x):  2  try:  
return  float(x)  
­­­­>  3 excepto  ValueError:
4
5 volver  x
TypeError:  el  argumento  float()  debe  ser  una  cadena  o  un  número,  no  una  'tupla'

Puede  capturar  varios  tipos  de  excepción  escribiendo  una  tupla  de  tipos  de  excepción  en  su  lugar  (los  paréntesis  son  
obligatorios):

def  intent_float(x):  try:  return  

float(x)  excepto  
(TypeError,  ValueError):
volver  x

En  algunos  casos,  es  posible  que  no  desee  suprimir  una  excepción,  pero  desea  que  se  ejecute  algún  código  
independientemente  de  si  el  código  en  el  bloque  de  prueba  tiene  éxito  o  no.  Para  hacer  esto,  use  finalmente:

f  =  abierto  (ruta,  'w')

prueba:  write_to_file  (f)

78  |  Capítulo  3:  Estructuras  de  datos,  funciones  y  archivos  integrados
Machine Translated by Google

finalmente:  
f.cerrar()

Aquí,  el  identificador  de  archivo  f  siempre  se  cerrará.  De  manera  similar,  puede  tener  un  código  que  se  ejecuta  solo  si  
el  bloque  try:  tiene  éxito  usando  else:

f  =  abierto  (ruta,  'w')

intente:  write_to_file(f)  
excepto:  
print('Failed')  else:  

print('Succeeded')  finalmente:  
f.close()

Excepciones  en  IPython

Si  se  genera  una  excepción  mientras  está  %ejecutando  un  script  o  ejecutando  cualquier  declaración,
IPython  imprimirá  de  forma  predeterminada  un  seguimiento  completo  de  la  pila  de  llamadas  (traceback)  con  unas  
pocas  líneas  de  contexto  alrededor  de  la  posición  en  cada  punto  de  la  pila:

En  [10]:  %ejecutar  ejemplos/ipython_bug.py
­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­  ­­­­­­­­­­­­­­­­­­­­­­­­­

AssertionError / Rastreo  (última  llamada  más  reciente)
home/wesm/code/pydata­book/examples/ipython_bug.py  en  <módulo>()  
13 throws_an_exception()
14
­­­>  15  llamando_cosas()

/home/wesm/code/pydata­book/examples/ipython_bug.py  in  llamar_cosas()  11  def  
llamar_cosas():  12
funciona_bien()  
­­­>  13 lanza_una_excepción()
14
15  llamando_cosas()

/home/wesm/code/pydata­book/examples/ipython_bug.py  en  throws_an_exception()
7 a  =  5  
8 b  =  6  
­­­­>  9 afirmar  (a  +  b  ==  10)
10  
11  def  llamando_cosas():

Error  de  aserción:

Tener  contexto  adicional  por  sí  mismo  es  una  gran  ventaja  sobre  el  intérprete  estándar  de  Python  (que  no  proporciona  
ningún  contexto  adicional).  Puede  controlar  la  cantidad  de  contexto  que  se  muestra  usando  el  comando  mágico  
%xmode ,  desde  Simple  (igual  que  el  intérprete  estándar  de  Python)  hasta  Verbose  (que  incluye  valores  de  argumento  
de  función  y

3.2  Funciones  |  79
Machine Translated by Google

más).  Como  verá  más  adelante  en  el  capítulo,  puede  pasar  a  la  pila  (usando  las  magias  %debug  o  %pdb )  después  de  que  

haya  ocurrido  un  error  para  la  depuración  post­mortem  interactiva.

3.3  Archivos  y  el  Sistema  Operativo
La  mayor  parte  de  este  libro  utiliza  herramientas  de  alto  nivel  como  pandas.read_csv  para  leer  archivos  de  datos  del  disco  
en  estructuras  de  datos  de  Python.  Sin  embargo,  es  importante  comprender  los  conceptos  básicos  de  cómo  trabajar  con  
archivos  en  Python.  Afortunadamente,  es  muy  simple,  lo  cual  es  una  de  las  razones  por  las  que  Python  es  tan  popular  para  
la  manipulación  de  archivos  y  texto.

Para  abrir  un  archivo  para  lectura  o  escritura,  utilice  la  función  de  apertura  integrada  con  una  ruta  de  archivo  relativa  o  
absoluta:

En  [207]:  ruta  =  'ejemplos/segismundo.txt'

En  [208]:  f  =  abrir  (ruta)

De  forma  predeterminada,  el  archivo  se  abre  en  modo  de  solo  lectura  'r'.  Luego  podemos  tratar  el  identificador  de  archivo  f  
como  una  lista  e  iterar  sobre  las  líneas  de  la  siguiente  manera:

para  línea  en  f:
aprobar

Las  líneas  salen  del  archivo  con  los  marcadores  de  fin  de  línea  (EOL)  intactos,  por  lo  que  a  menudo  verá  código  para  
obtener  una  lista  de  líneas  sin  EOL  en  un  archivo  como:

En  [209]:  líneas  =  [x.rstrip()  for  x  in  open(ruta)]

In  [210]:  líneas  
Out[210]:  
['Sueña  el  rico  en  su  riqueza',  'que  más  
cuidados  le  ofrece;',  '',  'sueña  el  pobre  que  

padece',  'su  miseria  y  su  pobreza;' ,  '',  
'sueña  el  que  a  medrar  empieza,',  

'sueña  el  que  afana  y  pretende,',  'sueña  el  que  
agravia  y  ofende',  '',  'y  en  el  mundo,  en  
conclusión,',  'todos  sueñan  lo  que  son,',  'aunque  

ninguno  lo  entiende.',  '']

Cuando  usa  abrir  para  crear  objetos  de  archivo,  es  importante  cerrar  explícitamente  el  archivo  cuando  haya  terminado  con  
él.  Al  cerrar  el  archivo,  se  liberan  sus  recursos  al  sistema  operativo:

En  [211]:  f.cerrar()

80  |  Capítulo  3:  Estructuras  de  datos,  funciones  y  archivos  integrados
Machine Translated by Google

Una  de  las  formas  de  facilitar  la  limpieza  de  archivos  abiertos  es  usar  la  instrucción  with :

En  [212]:  con  abierto  (ruta)  como  f:  
.....: líneas  =  [x.rstrip()  para  x  en  f]

Esto  cerrará  automáticamente  el  archivo  f  al  salir  del  bloque  with .

Si  hubiésemos  tecleado  f  =  open(ruta,  'w'),  se  habría  creado  un  nuevo  archivo  en  ejemplos/
segismundo.txt  (¡ojo!),  sobrescribiendo  cualquiera  en  su  lugar.  También  existe  el  modo  de  archivo  'x' ,  
que  crea  un  archivo  grabable  pero  falla  si  la  ruta  del  archivo  ya  existe.  Consulte  la  Tabla  3­3  para  
obtener  una  lista  de  todos  los  modos  válidos  de  lectura/escritura  de  archivos.

Para  archivos  legibles,  algunos  de  los  métodos  más  utilizados  son  leer,  buscar  y  decir.  read  
devuelve  un  cierto  número  de  caracteres  del  archivo.  Lo  que  constituye  un  "carácter"  está  
determinado  por  la  codificación  del  archivo  (p.  ej.,  UTF­8)  o  simplemente  bytes  sin  formato  
si  el  archivo  se  abre  en  modo  binario:
En  [213]:  f  =  abrir  (ruta)

En  [214]:  f.read(10)
Fuera[214]:  'Sueña  el  r'

En  [215]:  f2  =  open(ruta,  'rb')  #  Modo  binario

En  [216]:  f2.read(10)
Fuera[216]:  b'Sue\xc3\xb1a  el  '

El  método  de  lectura  avanza  la  posición  del  identificador  de  archivo  por  el  número  de  bytes  leídos.  Tell  
te  da  la  posición  actual:

En  [217]:  f.decir()
Salida[217]:  11

En  [218]:  f2.decir()
Salida[218]:  10

Aunque  leemos  10  caracteres  del  archivo,  la  posición  es  11  porque  se  necesitaron  tantos  bytes  para  
decodificar  10  caracteres  usando  la  codificación  predeterminada.  Puede  verificar  la  codificación  
predeterminada  en  el  módulo  sys :

En  [219]:  importar  sistema

En  [220]:  sys.getdefaultencoding()
Salida[220]:  'utf­8'

seek  cambia  la  posición  del  archivo  al  byte  indicado  en  el  archivo:

En  [221]:  f.buscar(3)
Salida[221]:  3

En  [222]:  f.read(1)
Fuera[222]:  'ñ'

3.3  Archivos  y  el  Sistema  Operativo  |  81
Machine Translated by Google

Por  último,  recordamos  cerrar  los  archivos:

En  [223]:  f.cerrar()

En  [224]:  f2.cerrar()

Tabla  3­3.  Modos  de  archivo  Python

Modo  Descripción

r Modo  de  solo  lectura

w modo  de  solo  escritura;  crea  un  nuevo  archivo  (borrando  los  datos  de  cualquier  archivo  con  el  mismo  nombre)

X modo  de  solo  escritura;  crea  un  nuevo  archivo,  pero  falla  si  la  ruta  del  archivo  ya  existe

a Agregar  al  archivo  existente  (crear  el  archivo  si  aún  no  existe)

r+ Lee  y  escribe

b Agregar  al  modo  para  archivos  binarios  (es  decir,  'rb'  o  'wb')

t Modo  de  texto  para  archivos  (decodificación  automática  de  bytes  a  Unicode).  Este  es  el  valor  predeterminado  si  no  se  especifica.  Agregue  

t  a  otros  modos  para  usar  esto  (es  decir,  'rt'  o  'xt')

Para  escribir  texto  en  un  archivo,  puede  usar  los  métodos  write  o  writelines  del  archivo .  Por  ejemplo,  podríamos  
crear  una  versión  de  prof_mod.py  sin  líneas  en  blanco  como  esta:

En  [225]:  con  open('tmp.txt',  'w')  como  identificador:
.....: handle.writelines(x  for  x  in  open(path)  if  len(x)  >  1)

En  [226]:  con  open('tmp.txt')  como  f:  líneas  =  
.....: f.readlines()

In  [227]:  líneas  
Out[227]:  
['Sueña  el  rico  en  su  riqueza,\n',  'que  más  
cuidados  le  ofrece;\n',  'sueña  el  pobre  que  
padecen\n',  'su  miseria  y  su  pobreza;\n',  
'sueña  el  que  a  medrar  empieza,\n',  
'sueña  el  que  afana  y  finge,\n',  'sueña  el  que  
agravia  y  ofende,\n',  'y  en  el  mundo,  en  
conclusión ,\n',  'todos  sueñan  lo  que  son,\n',  
'aunque  ninguno  lo  entiende.\n']

Consulte  la  Tabla  3­4  para  conocer  muchos  de  los  métodos  de  archivo  más  utilizados.

Tabla  3­4.  Métodos  o  atributos  importantes  del  archivo  de  Python

Método Descripción

leer  ([tamaño]) Devuelve  datos  del  archivo  como  una  cadena,  con  un  argumento  de  tamaño  opcional  que  indica  la  cantidad  de  

bytes  para  leer

líneas  de  lectura  ([tamaño]) Devuelve  la  lista  de  líneas  en  el  archivo,  con  argumento  de  tamaño  opcional

Escribir  la  cadena  pasada  en  el  archivo
escribir  (cadena)

82  |  Capítulo  3:  Estructuras  de  datos,  funciones  y  archivos  integrados
Machine Translated by Google

Método Descripción  

writelines(strings)  Escribe  la  secuencia  pasada  de  cadenas  en  el  archivo

cerca() Cierra  el  mango

enjuagar() Vacíe  el  búfer  de  E/S  interno  en  el  disco

buscar  (pos) Mover  a  la  posición  de  archivo  indicada  (entero)

decir() Devuelve  la  posición  actual  del  archivo  como  un  número  entero

cerrado Verdadero  si  el  archivo  está  cerrado

Bytes  y  Unicode  con  archivos  El  

comportamiento  predeterminado  de  los  archivos  de  Python  (ya  sean  de  lectura  o  escritura)  es  el  modo  
de  texto,  lo  que  significa  que  pretende  trabajar  con  cadenas  de  Python  (es  decir,  Unicode).  Esto  
contrasta  con  el  modo  binario,  que  puede  obtener  agregando  b  al  modo  de  archivo.
Veamos  el  archivo  (que  contiene  caracteres  no  ASCII  con  codificación  UTF­8)  de  la  sección  anterior:

En  [230]:  con  open(ruta)  como  f:  chars  
.....: =  f.read(10)

En  [231]:  caracteres
Fuera[231]:  'Sueña  el  r'

UTF­8  es  una  codificación  Unicode  de  longitud  variable,  por  lo  que  cuando  solicité  cierta  cantidad  de  
caracteres  del  archivo,  Python  lee  suficientes  bytes  (que  podrían  ser  tan  solo  10  o  tantos  como  40  
bytes)  del  archivo  para  decodificar  esa  cantidad  de  caracteres. .  Si  abro  el  archivo  en  modo  'rb' ,  lea  las  
solicitudes  con  el  número  exacto  de  bytes:

En  [232]:  con  open(ruta,  'rb')  como  f:  data  =  
.....: f.read(10)

En  [233]:  datos
Fuera[233]:  b'Sue\xc3\xb1a  el  '

Según  la  codificación  del  texto,  es  posible  que  pueda  decodificar  los  bytes  en  un  objeto  str  usted  mismo,  
pero  solo  si  cada  uno  de  los  caracteres  Unicode  codificados  está  completamente  formado:

En  [234]:  data.decode('utf8')
Fuera[234]:  'Sueña  el'

En  [235]:  datos[:4].decode('utf8')
­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­  ­­­­­­­­­­­­­­­­­­­­­­­­­

UnicodeDecodeError   Rastreo  (última  llamada  más  reciente)
<ipython­input­235­300e0af10bb7>  en  <módulo>()  ­­­­>  1  
datos[:4].decode('utf8')
UnicodeDecodeError:  el  códec  'utf­8'  no  puede  decodificar  el  byte  0xc3  en  la  posición  3:  final  inesperado  
de  los  datos

El  modo  de  texto,  combinado  con  la  opción  de  codificación  de  abierto,  proporciona  una  manera  
conveniente  de  convertir  de  una  codificación  Unicode  a  otra:

3.3  Archivos  y  el  Sistema  Operativo  |  83
Machine Translated by Google

En  [236]:  fregadero_ruta  =  'sink.txt'

En  [237]:  con  open(ruta)  como  fuente:
.....: con  open(sink_path,  'xt',  encoding='iso­8859­1')  como  sumidero:  
.....: sumidero.escribir(fuente.leer())

En  [238]:  con  open(sink_path,  encoding='iso­8859­1')  como  f:  print(f.read(10))
.....:
Sueña  el  r

Tenga  cuidado  al  usar  la  búsqueda  al  abrir  archivos  en  cualquier  modo  que  no  sea  binario.  Si  la  
posición  del  archivo  se  encuentra  en  medio  de  los  bytes  que  definen  un  carácter  Unicode,  las  lecturas  
posteriores  generarán  un  error:

En  [240]:  f  =  abrir  (camino)

En  [241]:  f.read(5)
Fuera[241]:  'Sueña'

En  [242]:  f.buscar(4)
Salida[242]:  4

En  [243]:  f.read(1)
­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­  ­­­­­­­­­­­­­­­­­­­­­­­­­

UnicodeDecodeError   Rastreo  (última  llamada  más  reciente)
<ipython­input­243­7841103e33f5>  en  <módulo>()  ­­­­>  1  
f.read(1) /miniconda/
envs/book­env/lib/python3.6/codecs.py  en  decode(self ,  entrada,  final)  #  decodificar  entrada  (teniendo  
319 en  cuenta  el  búfer)  data  =  self.buffer  +  input  (resultado,  
320 consumido)  =  
­­>  321 self._buffer_decode(data,  self.errors,  final
)
322 #  mantener  la  entrada  sin  decodificar  hasta  la  próxima  
323 llamada  self.buffer  =  data[consumed:]
UnicodeDecodeError:  el  códec  'utf­8'  no  puede  decodificar  el  byte  0xb1  en  la  posición  0:  byte  de  inicio  no  
válido

En  [244]:  f.cerrar()

Si  se  encuentra  realizando  regularmente  análisis  de  datos  en  datos  de  texto  que  no  son  ASCII,  será  
valioso  dominar  la  funcionalidad  Unicode  de  Python.  Consulte  la  documentación  en  línea  de  Python  
para  obtener  más  información.

3.4  Conclusión
Con  algunos  de  los  conceptos  básicos  y  el  entorno  y  el  lenguaje  de  Python  ahora  en  nuestro  haber,  es  
hora  de  seguir  adelante  y  aprender  sobre  NumPy  y  la  computación  orientada  a  matrices  en  Python.

84  |  Capítulo  3:  Estructuras  de  datos,  funciones  y  archivos  integrados
Machine Translated by Google

CAPÍTULO  4

Conceptos  básicos  de  NumPy:  matrices  y

Computación  Vectorizada

NumPy,  abreviatura  de  Numerical  Python,  es  uno  de  los  paquetes  fundamentales  más  importantes  
para  la  computación  numérica  en  Python.  La  mayoría  de  los  paquetes  computacionales  que  brindan  
funcionalidad  científica  utilizan  los  objetos  de  matriz  de  NumPy  como  lengua  franca  para  el  intercambio  
de  datos.

Estas  son  algunas  de  las  cosas  que  encontrarás  en  NumPy:

•  ndarray,  un  arreglo  multidimensional  eficiente  que  proporciona  operaciones  aritméticas  orientadas  
a  arreglos  rápidos  y  capacidades  de  transmisión  flexibles.

•  Funciones  matemáticas  para  operaciones  rápidas  en  arreglos  completos  de  datos  sin  tener  que  
escribir  bucles.  •  

Herramientas  para  leer/escribir  datos  de  matriz  en  el  disco  y  trabajar  con  mapas  de  memoria
archivos

•  Capacidades  de  álgebra  lineal,  generación  de  números  aleatorios  y  transformada  de  Fourier.  •  

AC  API  para  conectar  NumPy  con  bibliotecas  escritas  en  C,  C++  o  FORTRAN.

Debido  a  que  NumPy  proporciona  una  API  C  fácil  de  usar,  es  sencillo  pasar  datos  a  bibliotecas  externas  
escritas  en  un  lenguaje  de  bajo  nivel  y  también  para  que  las  bibliotecas  externas  devuelvan  datos  a  
Python  como  matrices  NumPy.  Esta  característica  ha  convertido  a  Python  en  el  lenguaje  de  elección  
para  envolver  bases  de  código  C/C++/Fortran  heredadas  y  brindarles  una  interfaz  dinámica  y  fácil  de  
usar.

Si  bien  NumPy  por  sí  mismo  no  proporciona  funciones  científicas  o  de  modelado,  comprender  los  
arreglos  de  NumPy  y  la  computación  orientada  a  arreglos  lo  ayudará  a  usar  herramientas  con  semántica  
orientada  a  arreglos,  como  pandas,  de  manera  mucho  más  efectiva.  Desde

85
Machine Translated by Google

NumPy  es  un  tema  amplio,  cubriré  muchas  funciones  avanzadas  de  NumPy  como  la  transmisión  con  más  profundidad  
más  adelante  (consulte  el  Apéndice  A).

Para  la  mayoría  de  las  aplicaciones  de  análisis  de  datos,  las  principales  áreas  de  funcionalidad  en  las  que  me  centraré  son:

•  Operaciones  de  matrices  vectorizadas  rápidas  para  la  recolección  y  limpieza  de  datos,  creación  de  subconjuntos  y
filtrado,  transformación  y  cualquier  otro  tipo  de  cómputo  •  Algoritmos  de  matrices  

comunes  como  operaciones  de  clasificación,  únicas  y  de  conjuntos  •  Estadísticas  descriptivas  

eficientes  y  agregación/resumen  de  datos  •  Alineación  de  datos  y  manipulaciones  de  datos  

relacionales  para  fusionar  y  unir
juntos  conjuntos  de  datos  heterogéneos

•  Expresar  lógica  condicional  como  expresiones  de  matriz  en  lugar  de  bucles  con  if­elif
otras  ramas

•  Manipulaciones  de  datos  grupales  (agregación,  transformación,  aplicación  de  funciones).
ción)

Si  bien  NumPy  proporciona  una  base  computacional  para  el  procesamiento  general  de  datos  numéricos,  muchos  
lectores  querrán  usar  pandas  como  base  para  la  mayoría  de  los  tipos  de  estadísticas  o  análisis,  especialmente  en  
datos  tabulares.  pandas  también  proporciona  algunas  funciones  más  específicas  de  dominio,  como  la  manipulación  
de  series  temporales,  que  no  está  presente  en  NumPy.

La  computación  orientada  a  matrices  en  Python  se  remonta  a  1995,  
cuando  Jim  Hugunin  creó  la  biblioteca  Numeric.  Durante  los  
siguientes  10  años,  muchas  comunidades  científicas  de  programación  
comenzaron  a  programar  matrices  en  Python,  pero  el  ecosistema  de  
la  biblioteca  se  fragmentó  a  principios  de  la  década  de  2000.  En  
2005,  Travis  Oliphant  pudo  forjar  el  proyecto  NumPy  a  partir  de  los  
entonces  proyectos  Numeric  y  Numarray  para  unir  a  la  comunidad  
en  torno  a  un  marco  informático  de  matriz  única.

Una  de  las  razones  por  las  que  NumPy  es  tan  importante  para  los  cálculos  numéricos  en  Python  es  porque  está  
diseñado  para  ser  eficiente  en  grandes  conjuntos  de  datos.  Hay  un  número  de  razones  para  esto:

•  NumPy  almacena  datos  internamente  en  un  bloque  de  memoria  contiguo,  independiente  de  otros  objetos  integrados  
de  Python.  La  biblioteca  de  algoritmos  de  NumPy  escrita  en  lenguaje  C  puede  operar  en  esta  memoria  sin  
ningún  tipo  de  verificación  u  otra  sobrecarga.
Las  matrices  NumPy  también  usan  mucha  menos  memoria  que  las  secuencias  integradas  de  Python.  

•  Las  operaciones  NumPy  realizan  cálculos  complejos  en  arreglos  completos  sin  necesidad  de  bucles  for  de  Python.

86  |  Capítulo  4:  Conceptos  básicos  de  NumPy:  arreglos  y  computación  vectorizada
Machine Translated by Google

Para  darle  una  idea  de  la  diferencia  de  rendimiento,  considere  una  matriz  NumPy  de  un  millón  de  
enteros  y  la  lista  Python  equivalente:

En  [7]:  importar  numpy  como  np

En  [8]:  my_arr  =  np.arange(1000000)

En  [9]:  mi_lista  =  lista(rango(1000000))

Ahora  multipliquemos  cada  secuencia  por  2:

_ my_arr2  =  my_arr  Tiempos  de  CPU:  usuario   *  2
In  [10]:  %time  for  in  range(10):  
20  ms,  sys:  50  ms,  total:  70  ms  Tiempo  de  pared:  72,4  ms

En  [11]:  %  de  tiempo  para   _ en  el  rango  (10):  mi_lista2  =  [x  *  2  para  x  en  mi_lista]
tiempos  de  CPU:  usuario  760  ms,  sys:  290  ms,  total:  1,05  s  Tiempo  de  
pared:  1,05  s

Los  algoritmos  basados  en  NumPy  son  generalmente  de  10  a  100  veces  más  rápidos  (o  más)  que  
sus  contrapartes  de  Python  puro  y  usan  mucha  menos  memoria.

4.1  NumPy  ndarray:  un  objeto  de  matriz  multidimensional
Una  de  las  características  clave  de  NumPy  es  su  objeto  de  matriz  N­dimensional,  o  ndarray,  que  es  
un  contenedor  rápido  y  flexible  para  grandes  conjuntos  de  datos  en  Python.  Las  matrices  le  permiten  
realizar  operaciones  matemáticas  en  bloques  completos  de  datos  utilizando  una  sintaxis  similar  a  
las  operaciones  equivalentes  entre  elementos  escalares.

Para  darle  una  idea  de  cómo  NumPy  permite  los  cálculos  por  lotes  con  una  sintaxis  similar  a  los  
valores  escalares  en  los  objetos  integrados  de  Python,  primero  importo  NumPy  y  genero  una  
pequeña  matriz  de  datos  aleatorios:

En  [12]:  importar  numpy  como  np

#  Generar  algunos  datos  aleatorios
En  [13]:  datos  =  np.random.randn(2,  3)

Entrada  [14]:  salida  
de  datos  
[14]:  matriz  ([[­0.2047,  0.4789,  ­0.5194],
[­0.5557,  1.9658,  1.3934]])

Luego  escribo  operaciones  matemáticas  con  datos:

Entrada  [15]:  datos  *  10  
Salida  [15]:  
matriz  ([[ ­2.0471,  4.7894,  ­5.1944],  [ ­5.5573,  19.6578,  
13.9341]])

En  [16]:  datos  +  datos
Fuera[16]:

4.1  NumPy  ndarray:  un  objeto  de  matriz  multidimensional  |  87
Machine Translated by Google

matriz  ([[­0.4094,  0.9579,  ­1.0389],
[­1.1115,  3.9316,  2.7868]])

En  el  primer  ejemplo,  todos  los  elementos  se  han  multiplicado  por  10.  En  el  segundo,  los  valores  
correspondientes  en  cada  "celda"  de  la  matriz  se  han  sumado  entre  sí.

En  este  capítulo  y  en  todo  el  libro,  uso  la  convención  NumPy  estándar  
de  usar  siempre  import  numpy  como  np.  Por  supuesto,  puedes  poner  
from  numpy  import  *  en  tu  código  para  evitar  tener  que  escribir  np.,  
pero  te  aconsejo  que  no  lo  conviertas  en  un  hábito.  El  espacio  de  
nombres  numpy  es  grande  y  contiene  una  serie  de  funciones  cuyos  
nombres  entran  en  conflicto  con  las  funciones  integradas  de  Python  
(como  min  y  max).

Un  ndarray  es  un  contenedor  multidimensional  genérico  para  datos  homogéneos;  es  decir,  todos  
los  elementos  deben  ser  del  mismo  tipo.  Cada  matriz  tiene  una  forma,  una  tupla  que  indica  el  
tamaño  de  cada  dimensión  y  un  dtype,  un  objeto  que  describe  el  tipo  de  datos  de  la  matriz:

Entrada  [17]:  data.shape  
Salida[17]:  (2,  3)

Entrada  [18]:  
datos.dtype  Salida[18]:  dtype('float64')
Este  capítulo  le  presentará  los  conceptos  básicos  del  uso  de  matrices  NumPy  y  debería  ser  
suficiente  para  continuar  con  el  resto  del  libro.  Si  bien  no  es  necesario  tener  una  comprensión  
profunda  de  NumPy  para  muchas  aplicaciones  de  análisis  de  datos,  volverse  competente  en  la  
programación  y  el  pensamiento  orientados  a  arreglos  es  un  paso  clave  en  el  camino  para  
convertirse  en  un  gurú  científico  de  Python.

Cada  vez  que  vea  "matriz",  "matriz  NumPy"  o  "ndarray"  en  el  texto,  
con  pocas  excepciones,  todos  se  refieren  a  lo  mismo:  el  objeto  ndarray.

Creación  de  ndarrays  La  

forma  más  sencilla  de  crear  una  matriz  es  utilizar  la  función  de  matriz .  Esto  acepta  cualquier  
objeto  similar  a  una  secuencia  (incluidas  otras  matrices)  y  produce  una  nueva  matriz  NumPy  que  
contiene  los  datos  pasados.  Por  ejemplo,  una  lista  es  un  buen  candidato  para  la  conversión:

En  [19]:  dato1  =  [6,  7.5,  8,  0,  1]

En  [20]:  arr1  =  np.array(datos1)

Entrada  [21]:  
arr1  Salida[21]:  matriz([ 6.,  7.5,  8. , 0. , 1. ])

88  |  Capítulo  4:  Conceptos  básicos  de  NumPy:  arreglos  y  computación  vectorizada
Machine Translated by Google

Las  secuencias  anidadas,  como  una  lista  de  listas  de  igual  longitud,  se  convertirán  en  una  matriz  
multidimensional:

En  [22]:  dato2  =  [[1,  2,  3,  4],  [5,  6,  7,  8]]

En  [23]:  matriz2  =  np.matriz(datos2)

En  [24]:  arr2
Salida[24]:  
matriz([[1,  2,  3,  4],  [5,  6,  
7,  8]])

Dado  que  data2  era  una  lista  de  listas,  la  matriz  NumPy  arr2  tiene  dos  dimensiones  con  forma  inferida  
de  los  datos.  Podemos  confirmar  esto  inspeccionando  los  atributos  ndim  y  shape :

Entrada  [25]:  arr2.ndim  
Salida  [25]:  2

Entrada  [26]:  arr2.forma  
Salida[26]:  (2,  4)

A  menos  que  se  especifique  explícitamente  (más  sobre  esto  más  adelante),  np.array  intenta  inferir  
un  buen  tipo  de  datos  para  la  matriz  que  crea.  El  tipo  de  datos  se  almacena  en  un  objeto  de  metadatos  
dtype  especial;  por  ejemplo,  en  los  dos  ejemplos  anteriores  tenemos:

Entrada  [27]:  arr1.dtype  
Salida[27]:  dtype('float64')

Entrada  [28]:  arr2.dtype  
Salida[28]:  dtype('int64')

Además  de  np.array,  hay  otras  funciones  para  crear  nuevas  matrices.  Como  ejemplos,  los  ceros  y  los  
unos  crean  matrices  de  0  o  1,  respectivamente,  con  una  longitud  o  forma  determinada.  vacío  crea  
una  matriz  sin  inicializar  sus  valores  a  ningún  valor  en  particular.  Para  crear  una  matriz  de  mayor  
dimensión  con  estos  métodos,  pase  una  tupla  para  la  forma:

En  [29]:  np.ceros(10)
Salida[29]:  matriz([ 0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.,  0.])

En  [30]:  np.ceros((3,  6))
Salida[30]:  
matriz([[ 0.,  0.,  0.,  0.,  0. ,  0.],  [ 0.,  0.,  0. ,  0.,  0.,  
0.],  [ 0 .,  0.,  0. ,  0. ,  0.,  0.]])

En  [31]:  np.empty((2,  3,  2))
Salida[31]:  
matriz([[[ 0.,  0.],  [ 0.,  
0.],  [ 0.,  0.]],

4.1  NumPy  ndarray:  un  objeto  de  matriz  multidimensional  |  89
Machine Translated by Google

[[ 0.,  0.],  [ 0.,  
0.],  [ 0.,  0.]]])

No  es  seguro  asumir  que  np.empty  devolverá  una  matriz  de  ceros.  
En  algunos  casos,  puede  devolver  valores  "basura"  no  inicializados.

arange  es  una  versión  con  valores  de  matriz  de  la  función  de  rango  integrada  de  Python :

En  [32]:  np.naranja(15)
Salida[32]:  matriz([ 0,  1,  2,  3,  4,  5 ,  6,  7,  8,  9,  10,  11,  12,  13,  14])

Consulte  la  Tabla  4­1  para  obtener  una  breve  lista  de  funciones  estándar  de  creación  de  arreglos.  Dado  
que  NumPy  se  centra  en  la  computación  numérica,  el  tipo  de  datos,  si  no  se  especifica,  en  muchos  
casos  será  float64  (coma  flotante).

Tabla  4­1.  Funciones  de  creación  de  matrices

Función Descripción

formación Convierta  los  datos  de  entrada  (lista,  tupla,  matriz  u  otro  tipo  de  secuencia)  en  un  ndarray  ya  sea  infiriendo  un  dtype  o  

especificando  explícitamente  un  dtype;  copia  los  datos  de  entrada  por  defecto

una  matriz Convierta  la  entrada  a  ndarray,  pero  no  copie  si  la  entrada  ya  es  un  ndarray

naranja Como  el  rango  incorporado  pero  devuelve  un  ndarray  en  lugar  de  una  lista

unos,   Produce  una  matriz  de  todos  los  1  con  la  forma  y  el  tipo  dados;  ones_like  toma  otra  matriz  y  produce  una  matriz  de  

unos_como unos  de  la  misma  forma  y  tipo

ceros,   Como  ones  y  ones_like  pero  produciendo  matrices  de  0s  en  su  lugar

ceros_como
vacío,   Cree  nuevas  matrices  asignando  nueva  memoria,  pero  no  las  complete  con  valores  como  unos  y

vacío  como   ceros
lleno, Producir  una  matriz  de  la  forma  y  tipo  dados  con  todos  los  valores  establecidos  en  el  "valor  de  relleno"  indicado  

full_like  full_like  toma  otra  matriz  y  produce  una  matriz  completa  de  la  misma  forma  y  tipo  de  d  ojo,  identidad  Cree  una  matriz  de  

identidad  cuadrada  N  ×  N  (1s  en  la  diagonal  y  ceros  en  otros  lugares)

Tipos  de  datos  para  ndarrays

El  tipo  de  datos  o  dtype  es  un  objeto  especial  que  contiene  la  información  (o  metadatos,  datos  sobre  
datos)  que  ndarray  necesita  para  interpretar  una  porción  de  memoria  como  un  tipo  particular  de  datos:

En  [33]:  arr1  =  np.array([1,  2,  3],  dtype=np.float64)

En  [34]:  arr2  =  np.array([1,  2,  3],  dtype=np.int32)

Entrada  [35]:  arr1.dtype  
Salida[35]:  dtype('float64')

90  |  Capítulo  4:  Conceptos  básicos  de  NumPy:  arreglos  y  computación  vectorizada
Machine Translated by Google

En  [36]:  arr2.dtipo
Salida[36]:  dtype('int32')

dtypes  son  una  fuente  de  flexibilidad  de  NumPy  para  interactuar  con  datos  provenientes  de  otros
sistemas  En  la  mayoría  de  los  casos,  proporcionan  una  asignación  directamente  a  un  disco  subyacente  o
representación  de  memoria,  lo  que  facilita  la  lectura  y  escritura  de  flujos  binarios  de  datos
a  disco  y  también  para  conectarse  a  código  escrito  en  un  lenguaje  de  bajo  nivel  como  C  o  Fortran.
Los  dtypes  numéricos  se  nombran  de  la  misma  manera:  un  nombre  de  tipo,  como  float  o  int,  sigue
seguido  por  un  número  que  indica  el  número  de  bits  por  elemento.  Un  valor  estándar  de  punto  flotante  
de  precisión  doble  (lo  que  se  usa  debajo  del  capó  en  el  objeto  flotante  de  Python )
ocupa  8  bytes  o  64  bits.  Por  lo  tanto,  este  tipo  se  conoce  en  NumPy  como  float64.  Ver
Tabla  4­2  para  obtener  una  lista  completa  de  los  tipos  de  datos  compatibles  con  NumPy.

No  se  preocupe  por  memorizar  los  tipos  de  dNumPy,  especialmente  si
eres  un  nuevo  usuario.  A  menudo  solo  es  necesario  preocuparse  por  el  general
tipo  de  datos  con  los  que  está  tratando,  ya  sea  punto  flotante,  complejo,
entero,  booleano,  cadena  u  objeto  general  de  Python.  Cuando  lo  necesites
más  control  sobre  cómo  se  almacenan  los  datos  en  la  memoria  y  en  el  disco,
conjuntos  de  datos  especialmente  grandes,  es  bueno  saber  que  tiene  el  control
sobre  el  tipo  de  almacenamiento.

Tabla  4­2.  Tipos  de  datos  NumPy

Escriba  el   Descripción

Escriba  int8,  uint8 código  i1,  u1 Tipos  enteros  de  8  bits  (1  byte)  con  y  sin  signo

int16,  uint16 i2,  u2 Tipos  enteros  de  16  bits  con  y  sin  signo

int32,  uint32 i4,  u4 Tipos  enteros  de  32  bits  con  y  sin  signo

int64,  uint64 i8,  u8 Tipos  enteros  de  64  bits  con  y  sin  signo

flotar16 f2 Punto  flotante  de  media  precisión

flotar32 f4  o  f Coma  flotante  estándar  de  precisión  simple;  compatible  con  flotador  C

flotar64 f8  o  d Punto  flotante  estándar  de  doble  precisión;  compatible  con  C  doble  y

Objeto  flotante  de  Python

flotar128 f16  o  g Punto  flotante  de  precisión  extendida

complejo64, c8,  c16,   Números  complejos  representados  por  dos  flotantes  de  32,  64  o  128,  respectivamente

complejo128, c32
complejo256
bool ? Tipo  booleano  que  almacena  valores  verdaderos  y  falsos

O
objeto tipo  de  objeto  Python;  un  valor  puede  ser  cualquier  objeto  Python

S
cadena_ Tipo  de  cadena  ASCII  de  longitud  fija  (1  byte  por  carácter);  por  ejemplo,  para  crear  un

cadena  dtype  con  longitud  10,  use  'S10'
unicode_ tu Tipo  Unicode  de  longitud  fija  (número  de  bytes  específico  de  la  plataforma);  mismo

especificación  semántica  como  string_  (p.  ej.,  'U10')

4.1  NumPy  ndarray:  un  objeto  de  matriz  multidimensional  |  91
Machine Translated by Google

Puede  convertir  o  convertir  explícitamente  una  matriz  de  un  dtype  a  otro  utilizando  el  método  
astype  de  ndarray :

En  [37]:  matriz  =  np.matriz([1,  2,  3,  4,  5])

Entrada  [38]:  arr.dtype  
Salida[38]:  dtype('int64')

En  [39]:  float_arr  =  arr.astype(np.float64)

Entrada  [40]:  float_arr.dtype  
Salida[40]:  dtype('float64')

En  este  ejemplo,  los  enteros  se  convirtieron  en  punto  flotante.  Si  hago  que  algunos  números  
de  coma  flotante  sean  de  tipo  entero,  la  parte  decimal  se  truncará:

En  [41]:  matriz  =  np.matriz([3.7,  ­1.2,  ­2.6,  0.5,  12.9,  10.1])

Entrada  [42]:  
matriz  Salida[42]:  matriz([ 3.7,  ­1.2,  ­2.6,  0.5,  12.9,  10.1])

En  [43]:  arr.astype(np.int32)
Salida[43]:  matriz([ 3,  ­1,  ­2,  0,  12,  10],  dtype=int32)

Si  tiene  una  matriz  de  cadenas  que  representan  números,  puede  usar  un  tipo  para  convertirlos  
a  forma  numérica:

En  [44]:  numeric_strings  =  np.array(['1.25',  '­9.6',  '42'],  dtype=np.string_)

En  [45]:  numeric_strings.astype(float)
Salida[45]:  matriz([ 1.25,  ­9.6  42. ]) ,

Es  importante  tener  cuidado  al  usar  el  tipo  numpy.string_ ,  ya  que  los  
datos  de  cadena  en  NumPy  tienen  un  tamaño  fijo  y  pueden  truncar  la  
entrada  sin  previo  aviso.  pandas  tiene  un  comportamiento  listo  para  usar  
más  intuitivo  en  datos  no  numéricos.

Si  la  conversión  fallara  por  alguna  razón  (como  una  cadena  que  no  se  puede  convertir  a  
float64),  se  generará  un  ValueError .  Aquí  fui  un  poco  perezoso  y  escribí  float  en  lugar  de  
np.float64;  NumPy  asigna  alias  a  los  tipos  de  Python  con  sus  propios  tipos  de  datos  equivalentes.

También  puede  usar  el  atributo  dtype  de  otra  matriz:

En  [46]:  int_array  =  np.arange(10)

En  [47]:  calibres  =  np.array([.22,  .270,  .357,  .380,  .44,  .50],  dtype=np.float64)

En  [48]:  int_array.astype(calibres.dtype)
Salida[48]:  matriz([ 0.,  1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9.])

92  |  Capítulo  4:  Conceptos  básicos  de  NumPy:  arreglos  y  computación  vectorizada
Machine Translated by Google

Hay  cadenas  de  código  de  tipo  abreviado  que  también  puede  usar  para  referirse  a  un  dtype:

En  [49]:  empty_uint32  =  np.empty(8,  dtype='u4')

En  [50]:  vacío_uint32
Fuera[50]:
0,  1075314688, 0,  1075707904,   0,
matriz([ 1075838976, 0,  1072693248],  dtype=uint32)

Llamar  a  astype  siempre  crea  una  nueva  matriz  (una  copia  de  los  datos),  incluso
si  el  nuevo  dtype  es  el  mismo  que  el  antiguo  dtype.

Aritmética  con  matrices  NumPy
Las  matrices  son  importantes  porque  le  permiten  expresar  operaciones  por  lotes  en  datos
sin  escribir  ningún  bucle  for .  Los  usuarios  de  NumPy  llaman  a  esto  vectorización.  Cualquier  aritmética
Las  operaciones  entre  matrices  de  igual  tamaño  aplican  la  operación  por  elementos:

En  [51]:  matriz  =  np.matriz([[1.,  2.,  3.],  [4.,  5.,  6.]])

En  [52]:  arr
Fuera[52]:
matriz([[ 1.,  2.,  3.],
[ 4.,  5.,  6.]])

Entrada  [53]:  Arr  
* Arr

Salida[53]:
matriz  ([[ 1.,  4.,  9.],
[ 16.,  25.,  36.]])

En  [54]:  arr  ­  arr
Fuera[54]:
matriz([[ 0.,  0.,  0.],
[ 0.,  0.,  0.]])

Las  operaciones  aritméticas  con  escalares  propagan  el  argumento  escalar  a  cada  elemento  en
la  matriz:

En  [55]:  1 /  arr
Fuera[55]:
matriz  ([[ 1.   , 0.5 ,  0,3333],
[ 0.25 , 0.2 ,  0,1667]])

Entrada  [56]:  Arr   **  0.5
Salida[56]:
matriz  ([[ 1.   ,  1.4142,  1.7321],
[ 2. ,  2.2361,  2.4495]])

Las  comparaciones  entre  matrices  del  mismo  tamaño  producen  matrices  booleanas:

4.1  NumPy  ndarray:  un  objeto  de  matriz  multidimensional  |  93
Machine Translated by Google

En  [57]:  matriz2  =  np.matriz([[0.,  4.,  1.],  [7.,  2.,  12.]])

Entrada  [58]:  
arr2  
Salida[58]:   4.,  1.],  2.,  
matriz([[ 0.,  
[ 7.,
12.]])

En  [59]:  arr2  >  arr
Salida[59]:  
array([[Falso,  Verdadero,  Falso],
[ Verdadero,  Falso,  Verdadero]],  dtype=bool)

Las  operaciones  entre  arreglos  de  diferentes  tamaños  se  llaman  transmisión  y  se  analizarán  con  más  
detalle  en  el  Apéndice  A.  No  es  necesario  tener  un  conocimiento  profundo  de  la  transmisión  para  la  mayor  
parte  de  este  libro.

Indexación  básica  y  corte  La  indexación  

de  matriz  NumPy  es  un  tema  rico,  ya  que  hay  muchas  maneras  en  las  que  puede  querer  seleccionar  un  
subconjunto  de  sus  datos  o  elementos  individuales.  Los  arreglos  unidimensionales  son  simples;  en  la  
superficie  actúan  de  manera  similar  a  las  listas  de  Python:

En  [60]:  arr  =  np.arange(10)

En  [61]:  arr
Salida[61]:  matriz([0,  1,  2,  3,  4,  5,  6,  7,  8,  9])

En  [62]:  arr[5]
Salida[62]:  5

En  [63]:  arr[5:8]
Salida[63]:  matriz([5,  6,  7])

En  [64]:  arr[5:8]  =  12

En  [65]:  arr
Salida[65]:  matriz([ 0,  1,  2,  3,  4,  12,  12,  12,  8,  9])

Como  puede  ver,  si  asigna  un  valor  escalar  a  un  segmento,  como  en  arr[5:8]  =  12,  el  valor  se  propaga  (o  
se  transmite  de  ahora  en  adelante)  a  toda  la  selección.  Una  primera  distinción  importante  de  las  listas  
integradas  de  Python  es  que  los  segmentos  de  matriz  son  vistas  en  la  matriz  original.
Esto  significa  que  los  datos  no  se  copian  y  cualquier  modificación  a  la  vista  se  reflejará  en  la  matriz  de  
origen.

Para  dar  un  ejemplo  de  esto,  primero  creo  una  porción  de  arr:

En  [66]:  arr_segmento  =  arr[5:8]

En  [67]:  arr_slice
Salida[67]:  matriz([12,  12,  12])

94  |  Capítulo  4:  Conceptos  básicos  de  NumPy:  arreglos  y  computación  vectorizada
Machine Translated by Google

Ahora,  cuando  cambio  valores  en  arr_slice,  las  mutaciones  se  reflejan  en  el  original

matriz  matriz :

En  [68]:  arr_slice[1]  =  12345

En  [69]:  arr
Salida[69]:  matriz([ 9]) 0, 1, 2, 3, 4, 12,  12345, 12, 8,

El  segmento  "desnudo" [:]  se  asignará  a  todos  los  valores  en  una  matriz:

En  [70]:  arr_slice[:]  =  64

En  [71]:  arr
Salida[71]:  matriz([ 0,  1,  2,  3,  4,  64,  64,  64,  8,  9])

Si  es  nuevo  en  NumPy,  es  posible  que  esto  le  sorprenda,  especialmente  si  ha  utilizado  otros  lenguajes  de  programación  
de  matrices  que  copian  datos  con  más  entusiasmo.  Como  NumPy  ha  sido  diseñado  para  poder  trabajar  con  matrices  muy  
grandes,  podría  imaginar  problemas  de  rendimiento  y  memoria  si  NumPy  insistiera  en  copiar  siempre  los  datos.

Si  desea  una  copia  de  una  porción  de  un  ndarray  en  lugar  de  
una  vista,  deberá  copiar  explícitamente  la  matriz,  por  ejemplo,  
arr[5:8].copy().

Con  matrices  de  mayor  dimensión,  tiene  muchas  más  opciones.  En  una  matriz  bidimensional,  los  elementos  en  cada  índice  
ya  no  son  escalares  sino  matrices  unidimensionales:

En  [72]:  matriz2d  =  np.matriz([[1,  2,  3],  [4,  5,  6],  [7,  8,  9]])

En  [73]:  arr2d[2]
Salida[73]:  matriz([7,  8,  9])

Por  lo  tanto,  se  puede  acceder  a  los  elementos  individuales  de  forma  recursiva.  Pero  eso  es  demasiado  trabajo,  por  lo  que  
puede  pasar  una  lista  de  índices  separados  por  comas  para  seleccionar  elementos  individuales.
Entonces  estos  son  equivalentes:

En  [74]:  arr2d[0][2]
Salida[74]:  3

En  [75]:  arr2d[0,  2]
Salida[75]:  3

Consulte  la  Figura  4­1  para  ver  una  ilustración  de  la  indexación  en  una  matriz  bidimensional.  Encuentro  útil  pensar  en  el  
eje  0  como  las  "filas"  de  la  matriz  y  el  eje  1  como  las  "columnas".

4.1  NumPy  ndarray:  un  objeto  de  matriz  multidimensional  |  95
Machine Translated by Google

Figura  4­1.  Elementos  de  indexación  en  una  matriz  NumPy

En  las  matrices  multidimensionales,  si  omite  los  índices  posteriores,  el  objeto  devuelto  será  un  ndarray  de  
dimensión  inferior  que  constará  de  todos  los  datos  de  las  dimensiones  superiores.  Entonces,  en  la  matriz  2  ×  2  
×  3  arr3d:

En  [76]:  matriz3d  =  np.matriz([[[1,  2,  3],  [4,  5,  6]],  [[7,  8,  9],  [10,  11,  12]]])

En  [77]:  arr3d
Salida[77]:  
matriz([[[ 1,  2,  3],  [ 4,  5,  
6]],  [[ 7,  8,  9],

[10,  11,  12]]])

arr3d[0]  es  una  matriz  de  2  ×  3:

En  [78]:  arr3d[0]
Salida[78]:  
matriz([[1,  2,  3],  [4,  
5,  6]])

Tanto  los  valores  escalares  como  las  matrices  se  pueden  asignar  a  arr3d[0]:

En  [79]:  valores_antiguos  =  arr3d[0].copy()

En  [80]:  arr3d[0]  =  42

En  [81]:  arr3d
Salida[81]:  
matriz([[[42,  42,  42],  [42,  
42,  42]],
[[ 7,  8,  9],
[10,  11,  12]]])

En  [82]:  arr3d[0]  =  valores_antiguos

96  |  Capítulo  4:  Conceptos  básicos  de  NumPy:  arreglos  y  computación  vectorizada
Machine Translated by Google

En  [83]:  arr3d
Salida[83]:  
matriz([[[ 1,  2,  3],  [ 4,  5,  
6]],
[[ 7,  8,  9],
[10,  11,  12]]])

De  manera  similar,  arr3d[1,  0]  le  brinda  todos  los  valores  cuyos  índices  comienzan  con  (1,  0),  formando  
una  matriz  unidimensional:

En  [84]:  arr3d[1,  0]
Salida[84]:  matriz([7,  8,  9])

Esta  expresión  es  la  misma  que  si  hubiéramos  indexado  en  dos  pasos:

En  [85]:  x  =  arr3d[1]

En  [86]:  x
Salida[86]:  
matriz([[ 7,  8,  9],  [10,  11,  
12]])

En  [87]:  x[0]
Salida[87]:  matriz([7,  8,  9])

Tenga  en  cuenta  que  en  todos  estos  casos  en  los  que  se  han  seleccionado  subsecciones  de  la  matriz,  
las  matrices  devueltas  son  vistas.

Indexación  con  cortes

Al  igual  que  los  objetos  unidimensionales,  como  las  listas  de  Python,  los  ndarrays  se  pueden  dividir  con  
la  sintaxis  familiar:

En  [88]:  arr
Salida[88]:  matriz([ 0,  1,  2,  3,  4,  64,  64,  64,  8,  9])

En  [89]:  arr[1:6]
Salida[89]:  matriz([ 1,  2,  3,  4,  64])

Considere  la  matriz  bidimensional  de  antes,  arr2d.  Cortar  esta  matriz  es  un  poco  diferente:

En  [90]:  arr2d
Salida[90]:  
matriz([[1,  2,  3],  [4,  
5,  6],  [7,  8,  
9]])

En  [91]:  arr2d[:2]
Salida[91]:  
matriz([[1,  2,  3],  [4,  
5,  6]])

4.1  NumPy  ndarray:  un  objeto  de  matriz  multidimensional  |  97
Machine Translated by Google

Como  puede  ver,  se  ha  cortado  a  lo  largo  del  eje  0,  el  primer  eje.  Por  lo  tanto,  un  corte  selecciona  un  rango  de  
elementos  a  lo  largo  de  un  eje.  Puede  ser  útil  leer  la  expresión  arr2d[:2]  como
"seleccione  las  dos  primeras  filas  de  arr2d".

Puede  pasar  varias  porciones  al  igual  que  puede  pasar  múltiples  índices:

En  [92]:  arr2d[:2,  1:]
Salida[92]:  
matriz([[2,  3],  [5,  
6]])

Al  cortar  de  esta  manera,  siempre  obtiene  vistas  de  matriz  del  mismo  número  de  dimensiones.  Al  mezclar  índices  
enteros  y  sectores,  se  obtienen  sectores  de  menor  dimensión.

Por  ejemplo,  puedo  seleccionar  la  segunda  fila  pero  solo  las  dos  primeras  columnas  así:

En  [93]:  arr2d[1,  :2]
Salida[93]:  matriz([4,  5])

De  manera  similar,  puedo  seleccionar  la  tercera  columna  pero  solo  las  dos  primeras  filas  así:

En  [94]:  arr2d[:2,  2]
Salida[94]:  matriz([3,  6])

Consulte  la  Figura  4­2  para  ver  una  ilustración.  Tenga  en  cuenta  que  los  dos  puntos  en  sí  mismos  significan  tomar  
todo  el  eje,  por  lo  que  puede  dividir  solo  los  ejes  de  dimensiones  superiores  haciendo:

En  [95]:  arr2d[:,  :1]
Salida[95]:  
matriz([[1],  
[4],  
[7]])

Por  supuesto,  la  asignación  a  una  expresión  de  sector  asigna  a  toda  la  selección:

En  [96]:  arr2d[:2,  1:]  =  0

En  [97]:  arr2d
Salida[97]:  
matriz([[1,  0,  0],  [4,  
0,  0],  [7,  8,  
9]])

98  |  Capítulo  4:  Conceptos  básicos  de  NumPy:  arreglos  y  computación  vectorizada
Machine Translated by Google

Figura  4­2.  Corte  de  matriz  bidimensional

Indexación  booleana  

Consideremos  un  ejemplo  en  el  que  tenemos  algunos  datos  en  una  matriz  y  una  matriz  de  
nombres  con  duplicados.  Voy  a  usar  aquí  la  función  randn  en  numpy.random  para  generar  
algunos  datos  aleatorios  normalmente  distribuidos:

En  [98]:  nombres  =  np.array(['Bob',  'Joe',  'Will',  'Bob',  'Will',  'Joe',  'Joe'])

En  [99]:  datos  =  np.random.randn(7,  4)

En  [100]:  nombres
Salida[100]:  
matriz(['Bob',  'Joe',  'Will',  'Bob',  'Will',  'Joe',  'Joe'],  dtype='<U4')

Entrada  [101]:  
salida  de  
datos  [101]:  matriz  ([[ 0.0929,  0.2817,  0.769 ,  1.2464],  
[ 1.0072,  ­1.2962,  0.275 ,  0.2289],  [ 1.3529,  
0.8864,  ­2.0016,  ­0.3718],  [ 1.669 ,  ­0,4386,  
­0,5397,  0,477 ],  [ 3,2489,  ­1,0212,  ­0,5771,  
0,1241],

4.1  NumPy  ndarray:  un  objeto  de  matriz  multidimensional  |  99
Machine Translated by Google

[ 0,3026,  0,5238,  0,0009,  1,3438],  [­0,7135,  
­0,8312,  ­2,3702,  ­1,8608]])

Supongamos  que  cada  nombre  corresponde  a  una  fila  en  la  matriz  de  datos  y  queremos  seleccionar  
todas  las  filas  con  el  nombre  correspondiente  'Bob'.  Al  igual  que  las  operaciones  aritméticas,  las  
comparaciones  (como  ==)  con  matrices  también  se  vectorizan.  Por  lo  tanto,  comparar  nombres  con  la  
cadena  'Bob'  produce  una  matriz  booleana:

En  [102]:  nombres  ==  'Bob'
Salida[102]:  matriz([ Verdadero,  Falso,  Falso,  Verdadero,  Falso,  Falso,  Falso],  dtype=bool)

Esta  matriz  booleana  se  puede  pasar  al  indexar  la  matriz:

En  [103]:  datos[nombres  ==  'Bob']
Salida[103]:  
matriz([[ 0.0929,  0.2817,  0.769  [ 1.669 ,   ,  1.2464],
­0.4386,  ­0.5397,  0.477 ]])

La  matriz  booleana  debe  tener  la  misma  longitud  que  el  eje  de  la  matriz  que  está  indexando.  Incluso  
puede  mezclar  y  combinar  matrices  booleanas  con  segmentos  o  enteros  (o  secuencias  de  enteros;  más  
sobre  esto  más  adelante).

La  selección  booleana  no  fallará  si  la  matriz  booleana  no  tiene  la  longitud  
correcta,  por  lo  que  recomiendo  tener  cuidado  al  usar  esta  función.

En  estos  ejemplos,  selecciono  de  las  filas  donde  nombres  ==  'Bob'  e  indexo  las  columnas  también:

En  [104]:  datos[nombres  ==  'Bob',  2:]
Salida[104]:  
matriz([[ 0.769 ,  1.2464],  
[­0.5397,  0.477 ]])

En  [105]:  datos[nombres  ==  'Bob',  3]
Salida[105]:  matriz([ 1.2464,  0.477 ])

Para  seleccionar  todo  menos  'Bob',  puede  usar !=  o  negar  la  condición  usando  ~:

En  [106]:  nombres !=  'Bob'
Salida[106]:  matriz([Falso,  Verdadero,  Verdadero,  Falso,  Verdadero,  Verdadero,  Verdadero],  dtype=bool)

En  [107]:  datos[~(nombres  ==  'Bob')]
Salida[107]:  
matriz([[ 1.0072,  ­1.2962,  0.275 ,  0.2289],  [ 1.3529,  
0.8864,  ­2.0016,  ­0.3718],  [ 3.2489 ,  ­1.0212 ,  
­0.5771,  0.1241],  [ 0.3026,  0.5238 ,  0.0009,  
1.3438],  [­0.7135,  ­0.8312,  ­2.3702,  ­1.8608]])

100  |  Capítulo  4:  Conceptos  básicos  de  NumPy:  arreglos  y  computación  vectorizada
Machine Translated by Google

El  operador  ~  puede  ser  útil  cuando  desea  invertir  una  condición  general:

En  [108]:  cond  =  nombres  ==  'Bob'

En  [109]:  datos[~cond]
Salida[109]:  
matriz([[ 1.0072,  ­1.2962,  0.275 ,  0.2289],  [ 1.3529,  
0.8864,  ­2.0016,  ­0.3718],  [ 3.2489 ,  ­1.0212 ,  
­0.5771,  0.1241],  [ 0.3026,  0.5238 ,  0.0009,  
1.3438],  [­0.7135,  ­0.8312,  ­2.3702,  ­1.8608]])

Seleccionando  dos  de  los  tres  nombres  para  combinar  múltiples  condiciones  booleanas,  use  operadores  
aritméticos  booleanos  como  &  (y)  y  |  (o):

En  [110]:  máscara  =  (nombres  ==  'Bob')  |  (nombres  ==  'Voluntad')

En  [111]:  máscara
Salida[111]:  matriz([ Verdadero,  Falso,  Verdadero,  Verdadero,  Verdadero,  Falso,  Falso],  dtype=bool)

En  [112]:  datos[máscara]
Salida[112]:  
matriz([[ 0.0929,  0.2817,  0.769 ,  1.2464],  [ 1.3529,  
0.8864,  ­2.0016,  ­0.3718],  [ 1.669 ,  ­0.4386,  
­0.5397,  0.477 ],  [ 3.2489,  ­  1.0212 ,  ­0.5771 ,  
0,1241]])

La  selección  de  datos  de  una  matriz  mediante  la  indexación  booleana  siempre  crea  una  copia  de  los  
datos,  incluso  si  la  matriz  devuelta  no  ha  cambiado.

Las  palabras  clave  de  Python  y  y  o  no  funcionan  con  matrices  booleanas.
Utilice  &  (y)  y  |  (o)  en  su  lugar.

Establecer  valores  con  matrices  booleanas  funciona  con  sentido  común.  Para  establecer  todos  los  
valores  negativos  en  los  datos  en  0,  solo  necesitamos  hacer:

En  [113]:  datos[datos  <  0]  =  0

Entrada  [114]:  
salida  de  
datos  [114]:  matriz  ([[ 0.0929,  0.2817,  0.769 ,  1.2464],  
0.2750.8864,  0.  0. ],  
[ 1.0072,  0. ,  0.2289],  [, 1.3529,  
[ 1.669  0.  0.  0.477 ],  [ 3.2489,  0. ,  ,0.1241],  
[ 0.3026,  0,.5238,  0.0009,  
, 1.3438],   ,[ 0. ]])
, 0.

, 0. , 0. , 0.

4.1  NumPy  ndarray:  un  objeto  de  matriz  multidimensional  |  101
Machine Translated by Google

Establecer  filas  o  columnas  completas  usando  una  matriz  booleana  unidimensional  también  es  fácil:

En  [115]:  datos[nombres !=  'Joe']  =  7

Entrada  [116]:  
salida  de  
7. [ 1.0072,  
datos  [116]:  matriz  ,([[ 7. ],   , 7. [ 7.  
, 7. 0. ,  0.2289],  
, 0,275  
7.  7. ],  [ 7. ],  [ 7. ],  [ 0.3026,  
0.5238,  0.0009,  
,
1.3438],  [ 0. ]]) , 7. ,
, 7. , 7. , 7.
, 7. , 7. , 7.

, 0. , 0. , 0.

Como  veremos  más  adelante,  este  tipo  de  operaciones  sobre  datos  bidimensionales  son  convenientes  
para  hacer  con  pandas.

Fancy  Indexing  Fancy  

indexing  es  un  término  adoptado  por  NumPy  para  describir  la  indexación  utilizando  matrices  de  enteros.
Supongamos  que  tuviéramos  una  matriz  de  8  ×  4:

En  [117]:  arr  =  np.vacío((8,  4))

En  [118]:  for  i  in  range(8):  arr[i]  =  i
.....:

Entrada  [119]:  
matriz  
Salida[119]:  matriz([[ 0.,  0.,  0.,  0.],  
[ 1.,  1.,  1.,  1.],  [ 2.,  2.,  2 .,  
2.],  [ 3.,  3.,  3.,  3.],  [ 4.,  4.,  
4.,  4.],  [ 5.,  5.,  5.,  5.],  [ 6 .,  
6.,  6.,  6.],  [ 7.,  7.,  7.,  7.]])

Para  seleccionar  un  subconjunto  de  las  filas  en  un  orden  particular,  simplemente  puede  pasar  una  lista  o  
ndarray  de  enteros  especificando  el  orden  deseado:

En  [120]:  arr[[4,  3,  0,  6]]
Salida[120]:  
matriz([[ 4.,  4.,  4.,  4.],  [ 3.,  3.,  3.,  
3.],  [ 0.,  0.,  0.,  0.],  [ 6.,  6.,  
6.,  6.]])

¡Ojalá  este  código  haya  hecho  lo  que  esperabas!  El  uso  de  índices  negativos  selecciona  filas  desde  el  final:

En  [121]:  arr[[­3,  ­5,  ­7]]
Fuera[121]:

102  |  Capítulo  4:  Conceptos  básicos  de  NumPy:  arreglos  y  computación  vectorizada
Machine Translated by Google

matriz  ([[ 5.,  5.,  5.,  5.],  [ 3.,  3.,  3.,  
3.],  [ 1.,  1.,  1.,  1.]])

Pasar  varias  matrices  de  índice  hace  algo  ligeramente  diferente;  selecciona  una  matriz  unidimensional  de  
elementos  correspondientes  a  cada  tupla  de  índices:

En  [122]:  arr  =  np.arange(32).reshape((8,  4))

En  [123]:  arr
Salida[123]:  
matriz([[ 0,  1,  2,  3],  [ 4,  5,  6,  
7],  [ 8,  9,  10,  11],  
[12,  13,  14,  15],  [16 ,  
17,  18,  19],  [20,  21,  
22,  23],  [24,  25,  26,  
27],  [28,  29,  30,  31]])

En  [124]:  arr[[1,  5,  7,  2],  [0,  3,  1,  2]]
Salida[124]:  matriz([ 4,  23,  29,  10])

Veremos  el  método  de  remodelación  con  más  detalle  en  el  Apéndice  A.

Aquí  se  seleccionaron  los  elementos  (1,  0),  (5,  3),  (7,  1)  y  (2,  2) .  Independientemente  de  cuántas  
dimensiones  tenga  la  matriz  (aquí,  solo  2),  el  resultado  de  la  indexación  elegante  siempre  es  unidimensional.

El  comportamiento  de  la  indexación  elegante  en  este  caso  es  un  poco  diferente  de  lo  que  algunos  usuarios  
podrían  haber  esperado  (incluido  yo  mismo),  que  es  la  región  rectangular  formada  al  seleccionar  un  
subconjunto  de  filas  y  columnas  de  la  matriz.  Aquí  hay  una  forma  de  conseguirlo:

En  [125]:  arr[[1,  5,  7,  2]][:,  [0,  3,  1,  2]]
Salida[125]:  
matriz([[ 4,  7,  5,  6],  [20,  23,  
21,  22],  [28,  31,  29,  
30],  [ 8,  11,  9,  10]])

Tenga  en  cuenta  que  la  indexación  sofisticada,  a  diferencia  del  corte,  siempre  copia  los  datos  en  una  nueva  
matriz.

Transposición  de  matrices  y  ejes  de  intercambio  La  

transposición  es  una  forma  especial  de  remodelación  que,  de  manera  similar,  devuelve  una  vista  de  los  
datos  subyacentes  sin  copiar  nada.  Las  matrices  tienen  el  método  de  transposición  y  también  el  atributo  T  
especial :

En  [126]:  arr  =  np.arange(15).reshape((3,  5))

En  [127]:  arr

4.1  NumPy  ndarray:  un  objeto  de  matriz  multidimensional  |  103
Machine Translated by Google

Salida[127]:  
matriz([[ 0,  1,  2,  3,  4],  [ 5,  6,  7,  8,  
9],  [10,  11,  12,  13,  14]])

Entrada  [128]:  
arreglo.T  
Salida[128]:  arreglo  ([[ 0,  
5,  10],  [ 1,  6,  
11],  [ 2,  7,  12],  
[ 3,  8,  13],  [ 4 ,  
9,  14]])

Al  realizar  cálculos  matriciales,  puede  hacerlo  muy  a  menudo,  por  ejemplo,  al  calcular  el  producto  de  la  
matriz  interna  mediante  np.dot:

En  [129]:  arr  =  np.random.randn(6,  3)

Entrada  [130]:  
matriz  
Salida[130]:  matriz  ([[­0.8608,  0.5601,  
­1.2659],  [ 0.1198,  ­1.0635,  
0.3329],  [­2.3594,  ­0.1995,  
­1.542 ],  [­0.9707,  ­1.307 ,  0,2863],  
[ 0,378 ,  ­0,7539,  0,3313],  [ 1,3497,  
0,0699,  0,2467]])

En  [131]:  np.dot(arr.T,  arr)
Salida[131]:  
matriz([[ 9.2291,  0.9394,  4.948 ],  [ 0.9394,  
3.7662,  ­1.3622],  [ 4.948 ,  ­1.3622,  
4.3437]])

Para  matrices  de  mayor  dimensión,  la  transposición  aceptará  una  tupla  de  números  de  eje  para  
permutar  los  ejes  (para  una  mayor  flexión  de  la  mente):

En  [132]:  arr  =  np.arange(16).reshape((2,  2,  4))

En  [133]:  arr
Salida[133]:  
matriz([[[ 0,  1,  2,  3],  [ 4,  5,  6,  
7]],
[[ 8,  9,  10,  11],
[12,  13,  14,  15]]])

En  [134]:  arr.transpose((1,  0,  2))
Salida[134]:  
matriz([[[ 0,  1,  2,  3],  [ 8,  9,  10,  
11]],
[[ 4,  5,  6,  7],
[12,  13,  14,  15]]])

104  |  Capítulo  4:  Conceptos  básicos  de  NumPy:  arreglos  y  computación  vectorizada
Machine Translated by Google

Aquí,  los  ejes  se  han  reordenado  con  el  segundo  eje  primero,  el  primer  eje  segundo,
y  el  último  eje  sin  cambios.

La  transposición  simple  con .T  es  un  caso  especial  de  intercambio  de  ejes.  ndarray  tiene  el  método
swapaxes,  que  toma  un  par  de  números  de  eje  y  cambia  los  ejes  indicados  a  la  parte  trasera.
rango  de  los  datos:

En  [135]:  arr
Fuera[135]:
matriz  ([[[ 0,  1,  2,  3],
[ 4,  5,  6,  7]],
[[ 8,  9,  10,  11],
[12,  13,  14,  15]]])

En  [136]:  arr.swapaxes(1,  2)
Fuera[136]:
matriz  ([[[ 0,  4],
[ 1,  5],
[ 2,  6],
[ 3,  7]],
[[ 8,  12],
[ 9,  13],
[10,  14],
[11,  15]]])

swapaxes  devuelve  de  manera  similar  una  vista  de  los  datos  sin  hacer  una  copia.

4.2  Funciones  universales:  matriz  rápida  de  elementos
Funciones
Una  función  universal,  o  ufunc,  es  una  función  que  realiza  operaciones  con  elementos
sobre  datos  en  ndarrays.  Puede  pensar  en  ellos  como  envoltorios  vectorizados  rápidos  para  simples
funciones  que  toman  uno  o  más  valores  escalares  y  producen  uno  o  más  resultados  escalares.

Muchos  ufuncs  son  simples  transformaciones  de  elementos,  como  sqrt  o  exp:

En  [137]:  arr  =  np.arange(10)

En  [138]:  arr
Salida[138]:  matriz([0,  1,  2,  3,  4,  5,  6,  7,  8,  9])

En  [139]:  np.sqrt(arr)
Fuera[139]:
,
matriz  ([ 0.  1. ,  1.4142,  
1.7321,  2.  2.6458,  2.8284,  3. ]) ,  2.2361,  2.4495,

En  [140]:  np.exp(arr)
Fuera[140]:
formación([ 1. , 2.7183,  7.3891,  20.0855,  54.5982,
148.4132,  403.4288,  1096.6332,  2980.958 ,  8103.0839])

4.2  Funciones  universales:  funciones  de  matriz  rápidas  basadas  en  elementos  |  105
Machine Translated by Google

Estos  se  denominan  ufuncs  unarios.  Otros,  como  agregar  o  máximo,  toman  dos  matrices
(por  lo  tanto,  ufuncs  binarios)  y  devuelve  una  sola  matriz  como  resultado:

En  [141]:  x  =  np.random.randn(8)

En  [142]:  y  =  np.random.randn(8)

En  [143]:  x
Fuera[143]:
matriz  ([­0.0119,  1.0048,  1.3272,  ­0.9193,  ­1.5491,  0.0222,  0.7584,
­0.6605])

En  [144]:  y
Fuera[144]:
matriz  ([ 0.8626,  ­0.01   , 0.05 ,  0.6702,  0.853 ,  ­0.9559,  ­0.0235,
­2.3042])

En  [145]:  np.maximum(x,  y)
Fuera[145]:
matriz  ([ 0.8626,  1.0048,  1.3272,  0.6702,  0.853  ­0.6605]) ,  0.0222,  0.7584,

Aquí,  numpy.maximum  calculó  el  máximo  por  elemento  de  los  elementos  en  x  y
y.

Si  bien  no  es  común,  un  ufunc  puede  devolver  varias  matrices.  modf  es  un  ejemplo,  un  vec
versión  torizada  del  divmod  integrado  de  Python ;  devuelve  el  fraccionario  y  el  integral
partes  de  una  matriz  de  punto  flotante:

En  [146]:  arr  =  np.random.randn(7)  *  5

En  [147]:  arr
Salida[147]:  matriz([­3.2623,  ­6.0915,  ­6.663 ,  5.3731,  3.6182,  3.45 ,  5.0077])

En  [148]:  resto,  parte_completa  =  np.modf(arr)

En  [149]:  resto
Salida[149]:  matriz([­0.2623,  ­0.0915,  ­0.663 ,  0,3731,  0,6182,  0,45 ,  0,0077])

En  [150]:  parte_completa
Salida[150]:  matriz([­3.,  ­6.,  ­6.,  5.,  3.,  3.,  5.])

Ufuncs  acepta  un  argumento  de  salida  opcional  que  les  permite  operar  en  el  lugar  en
arreglos:

En  [151]:  arr
Salida[151]:  matriz([­3.2623,  ­6.0915,  ­6.663 ,  5.3731,  3.6182,  3.45 ,  5.0077])

En  [152]:  np.sqrt(arr)
Salida[152]:  matriz([ yaya, yaya, nan,  2.318 ,  1.9022,  1.8574,  2.2378])

En  [153]:  np.sqrt(arr,  arr)

106  |  Capítulo  4:  Conceptos  básicos  de  NumPy:  arreglos  y  computación  vectorizada
Machine Translated by Google

Salida[153]:  matriz([ yaya, yaya, nan,  2.318 ,  1.9022,  1.8574,  2.2378])

En  [154]:  arr
Salida[154]:  matriz([ yaya, yaya, nan,  2.318 ,  1.9022,  1.8574,  2.2378])

Consulte  las  Tablas  4­3  y  4­4  para  obtener  una  lista  de  ufuncs  disponibles.

Tabla  4­3.  ufunciones  unarias

Función Descripción

abdominales,  fabulosos Calcule  el  valor  absoluto  por  elementos  para  valores  enteros,  de  punto  flotante  o  complejos

sqrt Calcule  la  raíz  cuadrada  de  cada  elemento  (equivalente  a  arr  **  0.5)

cuadrado Calcule  el  cuadrado  de  cada  elemento  (equivalente  a  arr  **  2)

Exp Calcule  el  exponente  ex  de  cada  elemento

registro,  registro10,   Logaritmo  natural  (base  e),  logaritmo  en  base  10,  logaritmo  en  base  2  y  log(1  +  x),  respectivamente

registro2,  registro1p

firmar Calcule  el  signo  de  cada  elemento:  1  (positivo),  0  (cero)  o  –1  (negativo)

fortificar  techo Calcule  el  techo  de  cada  elemento  (es  decir,  el  entero  más  pequeño  mayor  o  igual  que  ese

número)

piso Calcule  el  piso  de  cada  elemento  (es  decir,  el  entero  más  grande  menor  o  igual  a  cada  elemento)

imprimir Redondear  elementos  al  entero  más  cercano,  conservando  el  dtype
modelo Devolver  partes  fraccionarias  e  integrales  de  una  matriz  como  una  matriz  separada

isnan Devuelve  una  matriz  booleana  que  indica  si  cada  valor  es  NaN  (no  es  un  número)

isfinito,  isinf Devuelve  una  matriz  booleana  que  indica  si  cada  elemento  es  finito  (no  inf,  no  NaN)  o  infinito,

respectivamente

cos,  cosh,  pecado,   Funciones  trigonométricas  regulares  e  hiperbólicas

sinh,  bronceado,  tanh

arccos,  arccosh,  arcsin,   Funciones  trigonométricas  inversas

arcsinh,  arctan,  arctanh

lógico_no Calcule  el  valor  de  verdad  de  not  x  elemento­sabio  (equivalente  a  ~arr).

Tabla  4­4.  Funciones  universales  binarias

Función Descripción

agregar Agregar  elementos  correspondientes  en  matrices

sustraer Restar  elementos  en  la  segunda  matriz  de  la  primera  matriz

multiplicar Multiplicar  elementos  de  matriz

dividir,  piso_dividir Dividir  o  dividir  piso  (truncando  el  resto)

fuerza Eleve  los  elementos  de  la  primera  matriz  a  las  potencias  indicadas  en  la  segunda  matriz

máximo,  fmáx Máximo  por  elementos;  fmax  ignora  NaN

mínimo,  fmín Mínimo  elemental;  fmin  ignora  NaN
modificación Módulo  elemental  (resto  de  la  división)

copia  firmante Copie  el  signo  de  los  valores  en  el  segundo  argumento  a  los  valores  en  el  primer  argumento

4.2  Funciones  universales:  funciones  de  matriz  rápidas  basadas  en  elementos  |  107
Machine Translated by Google

Función Descripción

mayor,  mayor_igual,  menor,   Realice  una  comparación  por  elementos,  lo  que  genera  una  matriz  booleana  

menor_igual,  igual,   (equivalente  a  los  operadores  infijos  >,  >=,  <,  <=,  ==, !=)

no_igual
lógico_y,   Calcule  el  valor  de  verdad  de  los  elementos  de  la  operación  lógica  (equivalente  a  los  operadores  infijos)

lógico_o,  lógico_xor &  |,  ^)

4.3  Programación  orientada  a  matrices  con  matrices
El  uso  de  matrices  NumPy  le  permite  expresar  muchos  tipos  de  tareas  de  procesamiento  de  datos  como
expresiones  de  matriz  concisas  que  de  otro  modo  podrían  requerir  bucles  de  escritura.  Esta  práctica  de
reemplazar  bucles  explícitos  con  expresiones  de  matriz  se  conoce  comúnmente  como  vectorización.
ción  En  general,  las  operaciones  de  matrices  vectorizadas  suelen  ser  de  uno  o  dos  (o  más)  órdenes
de  magnitud  más  rápido  que  sus  equivalentes  puros  de  Python,  con  el  mayor  impacto  en
cualquier  tipo  de  cálculo  numérico.  Más  adelante,  en  el  Apéndice  A,  explico  la  radiodifusión,  un
poderoso  método  para  vectorizar  cálculos.

Como  ejemplo  simple,  supongamos  que  deseamos  evaluar  la  función  sqrt(x^2  +  y^2)
a  través  de  una  cuadrícula  regular  de  valores.  La  función  np.meshgrid  toma  dos  matrices  1D  y
produce  dos  matrices  2D  correspondientes  a  todos  los  pares  de  (x,  y)  en  las  dos  matrices:

En  [155]:  puntos  =  np.arange(­5,  5,  0.01)  #  1000  puntos  igualmente  espaciados

En  [156]:  xs,  ys  =  np.meshgrid(puntos,  puntos)

En  [157]:  sí
Fuera[157]:
matriz([[­5. ,  ­5.  ­5. ],­5. , ­5. , ..., ­5. , ,
[­4,99,  ­4,99,  ­4,99, ...,  ­4,99,  ­4,99,  ­4,99],
[­4,98,  ­4,98,  ­4,98, ...,  ­4,98,  ­4,98,  ­4,98],
...,
[ 4.97,  4.97,  4.97, ...,  4.97,  4.97,  4.97],
[ 4,98,  4,98,  4,98, ...,  4,98,  4,98,  4,98],
[ 4,99,  4,99,  4,99, ...,  4,99,  4,99,  4,99]])

Ahora,  evaluar  la  función  es  cuestión  de  escribir  la  misma  expresión  que  usarías
escribir  con  dos  puntos:

En  [158]:  z  =  np.sqrt(xs  **  2  +  ys  **  2)

En  [159]:  z
Fuera[159]:
matriz  ([[ 7.0711,  7.064 ,  7.0569, ...,  7.0499,  7.0569,  7.064 ],
[ 7.064 ,  7.0569,  7.0499, ...,  7.0428,  7.0499,  7.0569],
[ 7.0569,  7.0499,  7.0428, ...,  7.0357,  7.0428,  7.0499],
...,
[ 7.0499,  7.0428,  7.0357, ...,  7.0286,  7.0357,  7.0428],
[ 7.0569,  7.0499,  7.0428, ...,  7.0357,  7.0428,  7.0499],
[ 7.064 ,  7.0569,  7.0499, ...,  7.0428,  7.0499,  7.0569]])

108  |  Capítulo  4:  Conceptos  básicos  de  NumPy:  arreglos  y  computación  vectorizada
Machine Translated by Google

Como  vista  previa  del  Capítulo  9,  uso  matplotlib  para  crear  visualizaciones  de  esta  matriz  bidimensional:

En  [160]:  importar  matplotlib.pyplot  como  plt

En  [161]:  plt.imshow(z,  cmap=plt.cm.gray);  plt.colorbar()
Salida[161]:  <matplotlib.colorbar.Colorbar  en  0x7f715e3fa630>

En  [162]:  plt.title("  Gráfico  de  imagen  de  $\sqrt{x^2  +  y^2}$  para  una  cuadrícula  de  valores")
Salida[162]:  <matplotlib.text.Text  en  0x7f715d2de748>

Consulte  la  Figura  4­3.  Aquí  utilicé  la  función  matplotlib  imshow  para  crear  un  gráfico  de  imagen  a  partir  
de  una  matriz  bidimensional  de  valores  de  función.

Figura  4­3.  Parcela  de  función  evaluada  en  cuadrícula

Expresión  de  la  lógica  condicional  como  operaciones  de  matriz  La  función  

numpy.where  es  una  versión  vectorizada  de  la  expresión  ternaria  x  if  condition  else  y.  Supongamos  que  
tuviéramos  una  matriz  booleana  y  dos  matrices  de  valores:

4.3  Programación  orientada  a  matrices  con  matrices  |  109
Machine Translated by Google

En  [165]:  xarr  =  np.matriz([1.1,  1.2,  1.3,  1.4,  1.5])

En  [166]:  yarr  =  np.matriz([2.1,  2.2,  2.3,  2.4,  2.5])

En  [167]:  cond  =  np.array([Verdadero,  Falso,  Verdadero,  Verdadero,  Falso])

Supongamos  que  quisiéramos  tomar  un  valor  de  xarr  siempre  que  el  valor  correspondiente  en  
cond  sea  True  y,  de  lo  contrario,  tomar  el  valor  de  yarr.  Una  lista  de  comprensión  haciendo  esto  
podría  verse  así:

En  [168]:  resultado  =  [(x  si  c  sino  y)
.....: para  x,  y,  c  en  zip  (xarr,  yarr,  cond)]

Entrada  [169]:  
resultado  Salida[169]:  [1.1000000000000001,  2.2000000000000002,  1.3,  1.3999999999999999,  2.5]

Esto  tiene  múltiples  problemas.  Primero,  no  será  muy  rápido  para  matrices  grandes  (porque  
todo  el  trabajo  se  realiza  en  código  Python  interpretado).  En  segundo  lugar,  no  funcionará  con  
arreglos  multidimensionales.  Con  np.where  puedes  escribir  esto  de  manera  muy  concisa:

En  [170]:  resultado  =  np.where(cond,  xarr,  yarr)

Entrada  [171]:  
resultado  Salida[171]:  array([ 1.1,  2.2,  1.3,  1.4,  2.5])

El  segundo  y  tercer  argumento  de  np.where  no  necesitan  ser  matrices;  uno  o  ambos  pueden  ser  
escalares.  Un  uso  típico  de  where  en  el  análisis  de  datos  es  producir  una  nueva  matriz  de  
valores  basada  en  otra  matriz.  Suponga  que  tiene  una  matriz  de  datos  generados  aleatoriamente  
y  desea  reemplazar  todos  los  valores  positivos  con  2  y  todos  los  valores  negativos  con  ­2.  Esto  
es  muy  fácil  de  hacer  con  np.where:

En  [172]:  arr  =  np.random.randn(4,  4)

En  [173]:  Arr  
Out  [173]:  
Array  ([[ ­0.5031,  ­0.6223,  ­0.9212,  ­0.7262],  [ 0.2229,  
0.0513,  ­1.1577,  0.8167],  [ 0.4336,  1.0107,  
1.8249,  ­0.9975],  [ 0,8506,  ­0,1316,  0,9124,  
0,1882]])

En  [174]:  arr  >  0
Salida[174]:  
array([[Falso,  Falso,  Falso,  Falso],
[ Verdadero,  Verdadero,  Falso,  Verdadero],
[ Verdadero,  Verdadero,  Verdadero,  Falso],
[ Verdadero,  Falso,  Verdadero,  Verdadero]],  dtype=bool)

En  [175]:  np.where(arr  >  0,  2,  ­2)
Salida[175]:  
matriz([[­2,  ­2,  ­2,  ­2],  [ 2,  2,  
­2,  2],

110  |  Capítulo  4:  Conceptos  básicos  de  NumPy:  arreglos  y  computación  vectorizada
Machine Translated by Google

[ 2,  2,  2,  ­2],
[ 2,  ­2,  2,  2]])

Puede  combinar  escalares  y  matrices  al  usar  np.where.  Por  ejemplo,  puedo  reemplazar
todos  los  valores  positivos  en  arr  con  la  constante  2  así:

En  [176]:  np.where(arr  >  0,  2,  arr)  #  establecer  solo  valores  positivos  en  2
Fuera[176]:
matriz  ([[­0.5031,  ­0.6223,  ­0.9212,  ­0.7262],
[ 2. ], , 2. ,  ­1.1577,  2.  [ 2.  
,
2.  2. ,  ­0.9975], ,
[ 2. ,  ­0.1316,  2.  2. ]]) ,

Las  matrices  pasadas  a  np.where  pueden  ser  más  que  simples  matrices  o  escalares  del  mismo  tamaño.

Métodos  Matemáticos  y  Estadísticos
Un  conjunto  de  funciones  matemáticas  que  calculan  estadísticas  sobre  un  arreglo  completo  o  sobre
los  datos  a  lo  largo  de  un  eje  son  accesibles  como  métodos  de  la  clase  de  matriz.  Puede  usar  agregados
gaciones  (a  menudo  llamadas  reducciones)  como  suma,  media  y  std  (desviación  estándar)  ya  sea  por
llamando  al  método  de  instancia  de  matriz  o  usando  la  función  NumPy  de  nivel  superior.

Aquí  genero  algunos  datos  aleatorios  distribuidos  normalmente  y  calculo  algunos  agregados
Estadísticas:

En  [177]:  arr  =  np.random.randn(5,  4)

En  [178]:  arr
Fuera[178]:
matriz  ([[ 2.1695,  ­0.1149,  2.0037,  0.0296],
[ 0,7953,  0,1181,  ­0,7485,  0,585 ],
[ 0,1527,  ­1,5657,  ­0,5625,  ­0,0327],
[­0,929 ,  ­0,4826,  ­0,0363,  1,0954],
[ 0,9809,  ­0,5895,  1,5817,  ­0,5287]])

En  [179]:  arr.media()
Salida[179]:  0.19607051119998253

En  [180]:  np.mean(arr)
Salida[180]:  0.19607051119998253

En  [181]:  arr.sum()
Salida[181]:  3.9214102239996507

Funciones  como  mean  y  sum  toman  un  argumento  de  eje  opcional  que  calcula  la  estadística
tic  sobre  el  eje  dado,  lo  que  da  como  resultado  una  matriz  con  una  dimensión  menos:

En  [182]:  arr.media(eje=1)
Salida[182]:  matriz([ 1.022 ,  0.1875,  ­0.502 ,  ­0.0881,  0.3611])

En  [183]:  arr.sum(eje=0)
Salida[183]:  matriz([ 3.1693,  ­2.6345,  2.2381,  1.1486])

4.3  Programación  orientada  a  matrices  con  matrices  |  111
Machine Translated by Google

Aquí,  arr.mean(1)  significa  "calcular  la  media  de  las  columnas",  donde  arr.sum(0)  significa  "calcular  la  suma  de  las  
filas".

Otros  métodos  como  cumsum  y  cumprod  no  se  agregan,  sino  que  producen  una  matriz  de  resultados  intermedios:

En  [184]:  matriz  =  np.matriz  ([0,  1,  2,  3,  4,  5,  6,  7])

En  [185]:  arr.cumsum()
Salida[185]:  matriz([ 0,  1,  3,  6,  10,  15,  21,  28])

En  matrices  multidimensionales,  las  funciones  de  acumulación  como  cumsum  devuelven  una  matriz  del  mismo  
tamaño,  pero  con  los  agregados  parciales  calculados  a  lo  largo  del  eje  indicado  de  acuerdo  con  cada  segmento  
dimensional  inferior:

En  [186]:  matriz  =  np.matriz  ([[0,  1,  2],  [3,  4,  5],  [6,  7,  8]])

En  [187]:  arr
Salida[187]:  
matriz([[0,  1,  2],  [3,  4,  
5],  [6,  7,  8]])

En  [188]:  arr.cumsum(eje=0)
Salida[188]:  
matriz([[ 0,  1,  2],  [ 3,  5,  7],  
[ 9,  12,  15]])

En  [189]:  arr.cumprod(eje=1)
Salida[189]:  
matriz([[ 0,  0],  0,  [ 3,  12,  60],  [ 6,  
42,  336]])

Consulte  la  Tabla  4­5  para  obtener  una  lista  completa.  Veremos  muchos  ejemplos  de  estos  métodos  en  acción  en  
capítulos  posteriores.

Tabla  4­5.  Métodos  estadísticos  básicos  de  matriz

Método Descripción
suma Suma  de  todos  los  elementos  de  la  matriz  oa  lo  largo  de  un  eje;  las  matrices  de  longitud  cero  tienen  suma  0

significar
Significado  aritmetico;  las  matrices  de  longitud  cero  tienen  una  media  de  NaN

estándar,  variable Desviación  estándar  y  varianza,  respectivamente,  con  ajuste  opcional  de  grados  de  libertad  (denominador  
predeterminado  n)

min,  max   mínimo  y  máximo

argmin,  argmax  Índices  de  elementos  mínimo  y  máximo,  respectivamente
cumsum Suma  acumulada  de  elementos  a  partir  de  0

Cumprod Producto  acumulativo  de  elementos  a  partir  de  1

112  |  Capítulo  4:  Conceptos  básicos  de  NumPy:  arreglos  y  computación  vectorizada
Machine Translated by Google

Métodos  para  matrices  booleanas  Los  valores  

booleanos  se  convierten  en  1  (Verdadero)  y  0  (Falso)  en  los  métodos  anteriores.  Por  lo  tanto,  la  suma  se  usa  a  menudo  
como  un  medio  para  contar  valores  verdaderos  en  una  matriz  booleana:

En  [190]:  arr  =  np.random.randn(100)

In  [191]:  (arr  >  0).sum()  #  Número  de  valores  positivos  Out[191]:  42

Hay  dos  métodos  adicionales,  cualquiera  y  todos,  útiles  especialmente  para  arreglos  booleanos.  any  comprueba  si  uno  
o  más  valores  de  una  matriz  son  verdaderos,  mientras  que  all  comprueba  si  todos  los  valores  son  verdaderos:

En  [192]:  bools  =  np.array([Falso,  Falso,  Verdadero,  Falso])

En  [193]:  bools.any()
Salida[193]:  Verdadero

En  [194]:  bools.todo()
Salida[194]:  Falso

Estos  métodos  también  funcionan  con  matrices  no  booleanas,  donde  los  elementos  distintos  de  cero  evalúan
comió  a  Verdadero.

Clasificación

Al  igual  que  el  tipo  de  lista  integrado  de  Python,  las  matrices  NumPy  se  pueden  ordenar  en  el  lugar  con  el  método  de  
clasificación :

En  [195]:  arr  =  np.random.randn(6)

Entrada  [196]:  
matriz  Salida[196]:  matriz([ 0.6095,  ­0.4938,  1.24 ,  ­0.1357,  1.43 ,  ­0.8469])

En  [197]:  arr.sort()

Entrada  [198]:  
matriz  Salida[198]:  matriz([­0.8469,  ­0.4938,  ­0.1357,  0.6095,  1.24 , 1.43 ])

Puede  ordenar  cada  sección  unidimensional  de  valores  en  una  matriz  multidimensional  en  su  lugar  a  lo  largo  de  un  
eje  pasando  el  número  de  eje  para  ordenar:

En  [199]:  arr  =  np.random.randn(5,  3)

Entrada  [200]:  
matriz  
Salida[200]:  matriz  ([[ 0.6033,  1.2636,  
­0.2555],  [­0.4457,  0.4684,  
­0.9616],  [­1.8245,  0.6254,  1.0229],  
[ 1.1074,  0.0909,  ­0.3501 ] ,  
[ 0.218 ,  ­0.8948,  ­1.7415]])

4.3  Programación  orientada  a  matrices  con  matrices  |  113
Machine Translated by Google

En  [201]:  arr.sort(1)

Entrada  [202]:  
matriz  
Salida[202]:  matriz  ([[­0.2555,  0.6033,  
1.2636],  [­0.9616,  ­0.4457,  
0.4684],  [­1.8245,  0.6254,  1.0229],  
[­0.3501,  0.0909,  1.1074 ] ,  
[­1.7415,  ­0.8948,  0.218 ]])

El  método  de  nivel  superior  np.sort  devuelve  una  copia  ordenada  de  una  matriz  en  lugar  de  modificar  la  matriz  
en  el  lugar.  Una  forma  rápida  y  sucia  de  calcular  los  cuantiles  de  una  matriz  es  ordenarla  y  seleccionar  el  valor  
en  un  rango  particular:

En  [203]:  large_arr  =  np.random.randn(1000)

En  [204]:  large_arr.sort()

In  [205]:  large_arr[int(0.05  *  len(large_arr))]  #  5%  cuantil  Out[205]:  
­1.5311513550102103

Para  obtener  más  detalles  sobre  el  uso  de  los  métodos  de  clasificación  de  NumPy  y  técnicas  más  avanzadas  
como  las  clasificaciones  indirectas,  consulte  el  Apéndice  A.  En  pandas  también  se  pueden  encontrar  otros  tipos  
de  manipulación  de  datos  relacionados  con  la  clasificación  (p.  ej.,  clasificar  una  tabla  de  datos  por  una  o  más  
columnas). .

Unique  y  Other  Set  Logic  NumPy  tiene  

algunas  operaciones  básicas  de  conjuntos  para  ndarrays  unidimensionales.  Uno  de  uso  común  es  np.unique,  
que  devuelve  los  valores  únicos  ordenados  en  una  matriz:

En  [206]:  nombres  =  np.array(['Bob',  'Joe',  'Will',  'Bob',  'Will',  'Joe',  'Joe'])

En  [207]:  np.unique(nombres)
Salida[207]:  
matriz(['Bob',  'Joe',  'Will'],  
dtype='<U4')

En  [208]:  enteros  =  np.matriz([3,  3,  3,  2,  2,  1,  1,  4,  4])

En  [209]:  np.unique(ints)
Salida[209]:  matriz([1,  2,  3,  4])

Contraste  np.unique  con  la  alternativa  pura  de  Python:

En  [210]:  ordenados  (conjunto  (nombres))
Fuera[210]:  ['Bob',  'Joe',  'Will']

Otra  función,  np.in1d,  prueba  la  pertenencia  de  los  valores  de  una  matriz  a  otra  y  devuelve  una  matriz  booleana:

114  |  Capítulo  4:  Conceptos  básicos  de  NumPy:  arreglos  y  computación  vectorizada
Machine Translated by Google

En  [211]:  valores  =  np.array([6,  0,  0,  3,  2,  5,  6])

En  [212]:  np.in1d(valores,  [2,  3,  6])
Salida[212]:  matriz([ Verdadero,  Falso,  Falso,  Verdadero,  Verdadero,  Falso,  Verdadero],  dtype=bool)

Consulte  la  Tabla  4­6  para  obtener  una  lista  de  las  funciones  establecidas  en  NumPy.

Tabla  4­6.  Operaciones  de  conjuntos  de  matrices

Método Descripción

en  x   Calcule  los  elementos  únicos  ordenados  
unique(x)  intersect1d(x,  y)  Calcule  los  elementos  comunes  ordenados  en  x  e  y
union1d(x,  y) Calcular  la  unión  ordenada  de  elementos

en1d(x,  y) Calcule  una  matriz  booleana  que  indique  si  cada  elemento  de  x  está  contenido  en  y

setdiff1d(x,  y) Diferencia  de  conjuntos,  elementos  en  x  que  no  están  en  y

conjuntoxor1d(x,  y) Establecer  diferencias  simétricas;  elementos  que  están  en  cualquiera  de  las  matrices,  pero  no  en  ambas

4.4  Entrada  y  salida  de  archivos  con  matrices
NumPy  puede  guardar  y  cargar  datos  desde  y  hacia  el  disco,  ya  sea  en  formato  de  texto  o  binario.
En  esta  sección  solo  analizo  el  formato  binario  incorporado  de  NumPy,  ya  que  la  mayoría  de  los  usuarios  
preferirán  pandas  y  otras  herramientas  para  cargar  texto  o  datos  tabulares  (consulte  el  Capítulo  6  para  obtener  
más  información).

np.save  y  np.load  son  las  dos  funciones  de  caballo  de  batalla  para  guardar  y  cargar  datos  de  matriz  en  el  disco  
de  manera  eficiente.  Las  matrices  se  guardan  de  forma  predeterminada  en  un  formato  binario  sin  comprimir  con  
la  extensión  de  archivo .npy:

En  [213]:  arr  =  np.arange(10)

En  [214]:  np.save('some_array',  arr)

Si  la  ruta  del  archivo  aún  no  termina  en .npy,  se  agregará  la  extensión.  La  matriz  en  el  disco  se  puede  cargar  con  
np.load:

En  [215]:  np.load('some_array.npy')
Salida[215]:  matriz([0,  1,  2,  3,  4,  5,  6,  7,  8,  9])

Guarde  varias  matrices  en  un  archivo  sin  comprimir  usando  np.savez  y  pasando  las  matrices  como  argumentos  
de  palabras  clave:

En  [216]:  np.savez('array_archive.npz',  a=arr,  b=arr)

Al  cargar  un  archivo .npz,  obtiene  un  objeto  similar  a  un  dictado  que  carga  las  matrices  individuales  de  forma  
perezosa:

En  [217]:  arch  =  np.load('array_archive.npz')

En  [218]:  arco['b']
Salida[218]:  matriz([0,  1,  2,  3,  4,  5,  6,  7,  8,  9])

4.4  Entrada  y  salida  de  archivos  con  matrices  |  115
Machine Translated by Google

Si  sus  datos  se  comprimen  bien,  es  posible  que  desee  utilizar  numpy.savez_compressed  en  su  lugar:

En  [219]:  np.savez_compressed('arrays_compressed.npz',  a=arr,  b=arr)

4.5  Álgebra  lineal
El  álgebra  lineal,  como  la  multiplicación  de  matrices,  las  descomposiciones,  los  determinantes  y  otras  
matemáticas  de  matrices  cuadradas,  es  una  parte  importante  de  cualquier  biblioteca  de  matrices.  A  
diferencia  de  algunos  lenguajes  como  MATLAB,  multiplicar  dos  matrices  bidimensionales  con  *  es  un  
producto  de  elementos  en  lugar  de  un  producto  de  punto  de  matriz.  Por  lo  tanto,  hay  un  punto  de  función,  
tanto  un  método  de  matriz  como  una  función  en  el  espacio  de  nombres  numpy ,  para  la  multiplicación  de  matrices:

En  [223]:  x  =  np.matriz([[1.,  2.,  3.],  [4.,  5.,  6.]])

En  [224]:  y  =  np.matriz([[6.,  23.],  [­1,  7],  [8,  9]])

Entrada  
[225]:  x  
Salida[225]:  array([[ 1.,  2.,  
3.],  [ 4.,  5.,  6.]])

Entrada  
[226]:  y  
Salida[226]:  array([[ 6.,  
23.],  [ ­1.,  7.],  
[ 8.,  9.]])

En  [227]:  x.punto(y)
Salida[227]:  
matriz([[ 28.,  64.],  [ 67.,  
181.]])

x.dot(y)  es  equivalente  a  np.dot(x,  y):
En  [228]:  np.dot(x,  y)
Salida[228]:  
matriz([[ 28.,  64.],  [ 67.,  
181.]])

Un  producto  matricial  entre  una  matriz  bidimensional  y  una  matriz  unidimensional  de  tamaño  adecuado  da  
como  resultado  una  matriz  unidimensional:

En  [229]:  np.dot(x,  np.ones(3))
Salida[229]:  matriz([ 6.,  15.])

El  símbolo  @  (a  partir  de  Python  3.5)  también  funciona  como  un  operador  infijo  que  realiza  la  multiplicación  
de  matrices:

En  [230]:  x  @  np.ones(3)
Salida[230]:  matriz([ 6.,  15.])

116  |  Capítulo  4:  Conceptos  básicos  de  NumPy:  arreglos  y  computación  vectorizada
Machine Translated by Google

numpy.linalg  tiene  un  conjunto  estándar  de  descomposición  de  matrices  y  cosas  como  
inversa  y  determinante.  Estos  se  implementan  bajo  el  capó  a  través  de  las  mismas  
bibliotecas  de  álgebra  lineal  estándar  de  la  industria  utilizadas  en  otros  lenguajes  como  
MATLAB  y  R,  como  BLAS,  LAPACK  o  posiblemente  (dependiendo  de  su  compilación  
NumPy)  Intel  MKL  (Math  Kernel  Library)  propietario:
En  [231]:  from  numpy.linalg  import  inv,  qr

En  [232]:  X  =  np.random.randn(5,  5)

En  [233]:  mat  =  XTdot(X)

En  [234]:  inv(tapete)
Salida[234]:  
matriz([[ 933.1189,  871.8258,  ­1417.6902,  ­1460.4005,  1782.1391],  [ 871.8258,  
815.3929,  ­1325.9965,  ­1365.9242 ,  1666.9347],  [­  1417.6902,  ­1325.9965,  
2158.4424,  2222.0191,  ­2711.6822] ,  [­1460.4005,  ­1365.9242,  2222.0191,  
2289.0575,  ­2793.422 ],  [ 1782.1391,  1666.9347,  ­2711.6822,  ­2793.422
,  3409.5128]])

En  [235]:  mat.dot(inv(mat))
Salida[235]:  
matriz([[ 1.,  0.,  ­0.,  ­0.,  ­0.],  [­0.,  1.,  0.,  0.,  
0.],  [ 0.,  0.,  1.,  0.,  0.],  [­0.,  0.,  0.,  
1.,  ­0.],  [­0.,  0.,  0.,  0.,  1.]] )

En  [236]:  q,  r  =  qr(mat)

Entrada  [237]:  
r  
Salida[237]:  array([[­1.6914,   ,  0.1757,  0.4075,  ­0.7838], ,  
4.38  [ 0. ,  ­2.6436,  0.1939,  ­3.072  [ 0.  [ 0.   ­1.0702], ,  
[ 0. , 0. ­0.8138,  1.5414,  0.6155], ,  ­2.6445,  
, 0. , 0. ­2.1669],  0. ,  0.0002]])
, 0. , 0. ,
La  expresión  XTdot(X)  calcula  el  producto  escalar  de  X  con  su  transpuesta  XT

Consulte  la  tabla  4­7  para  obtener  una  lista  de  algunas  de  las  funciones  de  álgebra  lineal  más  utilizadas.

Tabla  4­7.  Funciones  numpy.linalg  comúnmente  utilizadas

Función  descriptiva

diagnóstico Devuelva  los  elementos  diagonales  (o  fuera  de  la  diagonal)  de  una  matriz  cuadrada  como  una  matriz  1D,  o  convierta  una  matriz  1D  

en  una  matriz  cuadrada  con  ceros  fuera  de  la  diagonal

punto Traza  de  la  multiplicación  

de  matrices  Calcular  la  suma  de  los  elementos  de  la  diagonal

det Calcule  el  determinante  de  la  matriz

4.5  Álgebra  lineal  |  117
Machine Translated by Google

Función  descriptiva

eig Calcule  los  valores  propios  y  los  vectores  propios  de  una  matriz  cuadrada

inversión Calcular  la  inversa  de  una  matriz  cuadrada

Pinv Calcule  la  pseudo­inversa  de  Moore­Penrose  de  una  matriz

qr Calcular  la  descomposición  QR

svd Calcule  la  descomposición  en  valores  singulares  (SVD)

solve  Resolver  el  sistema  lineal  Ax  =  b  para  x,  donde  A  es  una  matriz  cuadrada  

lstsq  Calcular  la  solución  de  mínimos  cuadrados  para  Ax  =  b

4.6  Generación  de  números  pseudoaleatorios
El  módulo  numpy.random  complementa  el  Python  aleatorio  incorporado  con  funciones  para  
generar  de  manera  eficiente  matrices  completas  de  valores  de  muestra  de  muchos  tipos  de  
distribuciones  de  probabilidad.  Por  ejemplo,  puede  obtener  una  matriz  de  muestras  de  4  ×  4  de  
la  distribución  normal  estándar  usando  normal:

En  [238]:  muestras  =  np.random.normal(size=(4,  4))

In  [239]:  muestras  
Out[239]:  
array([[ 0.5732,  0.1933,  0.4429,  1.2796],  [ 0.575 ,  0.4339,  
­0.7658,  ­1.237 ],  [­0.5367,  1.8545,  ­0.92 ,  
­0.1082],  [ 0,1525,  0,9435,  ­1,0953,  ­0,144 ]])

El  módulo  aleatorio  incorporado  de  Python ,  por  el  contrario,  solo  muestra  un  valor  a  la  vez.  
Como  puede  ver  en  este  punto  de  referencia,  numpy.random  es  mucho  más  rápido  en  un  orden  
de  magnitud  para  generar  muestras  muy  grandes:

En  [240]:  from  random  import  normalvariate

En  [241]:  N  =  1000000

En  [242]:  %timeit  muestras  =  [normalvariate(0,  1)  for  1.77  s  +­  126   _ en  el  rango  (N)]
ms  per  loop  (media  +­  desv.  estándar  de  7  ejecuciones,  1  loop  cada  una)

En  [243]:  %timeit  np.random.normal(size=N)  61,7  ms  
+­  1,32  ms  por  bucle  (media  +­  desviación  estándar  de  7  ejecuciones ,  10  bucles  cada  una)

Decimos  que  son  números  pseudoaleatorios  porque  son  generados  por  un  algoritmo  con  
comportamiento  determinista  basado  en  la  semilla  del  generador  de  números  aleatorios.  Puede  
cambiar  la  semilla  de  generación  de  números  aleatorios  de  NumPy  usando  np.random.seed:

En  [244]:  np.random.seed(1234)

118  |  Capítulo  4:  Conceptos  básicos  de  NumPy:  arreglos  y  computación  vectorizada
Machine Translated by Google

Las  funciones  de  generación  de  datos  en  numpy.random  usan  una  semilla  aleatoria  global.  Para  evitar  el  
estado  global,  puede  usar  numpy.random.RandomState  para  crear  un  generador  de  números  aleatorios  aislado  
de  los  demás:

En  [245]:  rng  =  np.random.RandomState(1234)

En  [246]:  rng.randn(10)
Salida[246]:  
matriz([ 0.4714,  ­1.191 ,  1,4327,  ­0,3127,  ­0,7206 ,  0,8872 ,  0,8596,  ­0,6365 ,  0,0157,  
­2,2427])

Consulte  la  Tabla  4­8  para  obtener  una  lista  parcial  de  las  funciones  disponibles  en  numpy.random.  Daré  
algunos  ejemplos  de  cómo  aprovechar  la  capacidad  de  estas  funciones  para  generar  grandes  matrices  de  
muestras  de  una  vez  en  la  siguiente  sección.

Tabla  4­8.  Lista  parcial  de  funciones  numpy.random
Función Descripción

semilla Sembrar  la  permutación  del  generador  de  

números  aleatorios  Devolver  una  permutación  aleatoria  de  una  secuencia,  o  devolver  un  rango  permutado

barajar Permutar  aleatoriamente  una  secuencia  en  el  lugar

rand Extraer  muestras  de  una  distribución  uniforme

al  azar Dibuja  números  enteros  aleatorios  de  un  rango  bajo  a  alto  dado

rancio Extraiga  muestras  de  una  distribución  normal  con  media  0  y  desviación  estándar  1  (interfaz  similar  a  MATLAB)

binomio Extraer  muestras  de  una  distribución  binomial

normal Extraiga  muestras  de  una  distribución  normal  (gaussiana)

beta Extraer  muestras  de  una  distribución  beta

chi  cuadrado Extraer  muestras  de  una  distribución  de  chi­cuadrado

gama Extraer  muestras  de  una  distribución  gamma

uniforme Extraiga  muestras  de  una  distribución  uniforme  [0,  1)

4.7  Ejemplo:  paseos  aleatorios
La  simulación  de  paseos  aleatorios  proporciona  una  aplicación  ilustrativa  de  la  utilización  de  operaciones  de  
matriz.  Consideremos  primero  un  paseo  aleatorio  simple  que  comienza  en  0  con  pasos  de  1  y  –1  que  ocurren  
con  la  misma  probabilidad.

Aquí  hay  una  forma  pura  de  Python  para  implementar  una  sola  caminata  aleatoria  con  1,000  pasos  usando  el  
módulo  aleatorio  incorporado :

En  [247]:  importar  aleatorio .....:  
posición  =  0 .....:  caminar  =  
[posición] .....:  pasos  =  1000 .....:  
para  i  en  rango  (pasos):  
paso  =  1  si  random.randint(0,  1)  else  ­1  
.....: posición  +=  paso
.....:

4.7  Ejemplo:  Paseos  aleatorios  |  119
Machine Translated by Google

.....: caminar.añadir(posición)
.....:

Consulte  la  Figura  4­4  para  ver  un  gráfico  de  ejemplo  de  los  primeros  100  valores  en  uno  de  estos  recorridos  
aleatorios:

En  [249]:  plt.plot(caminar[:100])

Figura  4­4.  Un  simple  paseo  aleatorio

Puede  hacer  la  observación  de  que  la  caminata  es  simplemente  la  suma  acumulativa  de  los  pasos  aleatorios  y  
podría  evaluarse  como  una  expresión  de  matriz.  Por  lo  tanto,  uso  el  módulo  np.random  para  dibujar  1,000  
lanzamientos  de  monedas  a  la  vez,  los  establezco  en  1  y  ­1,  y  calculo  la  suma  acumulada:

En  [251]:  npasos  =  1000

En  [252]:  sorteos  =  np.random.randint(0,  2,  tamaño=npasos)

En  [253]:  pasos  =  np.where(dibuja  >  0,  1,  ­1)

En  [254]:  caminar  =  pasos.cumsum()
A  partir  de  esto  podemos  comenzar  a  extraer  estadísticas  como  el  valor  mínimo  y  máximo  a  lo  largo  de  la  
trayectoria  de  la  caminata:

En  [255]:  caminar.min()
Salida[255]:  ­3

120  |  Capítulo  4:  Conceptos  básicos  de  NumPy:  arreglos  y  computación  vectorizada
Machine Translated by Google

En  [256]:  caminar.max()
Salida[256]:  31

Una  estadística  más  complicada  es  el  primer  tiempo  de  cruce,  el  paso  en  el  que  la  caminata  aleatoria  
alcanza  un  valor  particular.  Aquí  podríamos  querer  saber  cuánto  tiempo  le  tomó  a  la  caminata  aleatoria  
alejarse  al  menos  10  pasos  del  origen  0  en  cualquier  dirección.  np.abs(caminar)  >=  10  nos  da  una  
matriz  booleana  que  indica  dónde  la  caminata  ha  alcanzado  o  excedido  10,  pero  queremos  el  índice  
de  los  primeros  10  o  –10.  Resulta  que  podemos  calcular  esto  usando  argmax,  que  devuelve  el  primer  
índice  del  valor  máximo  en  la  matriz  booleana  (Verdadero  es  el  valor  máximo):

En  [257]:  (np.abs(caminar)  >=  10).argmax()
Salida[257]:  37

Tenga  en  cuenta  que  usar  argmax  aquí  no  siempre  es  eficiente  porque  siempre  realiza  un  escaneo  
completo  de  la  matriz.  En  este  caso  especial,  una  vez  que  se  observa  un  Verdadero  sabemos  que  es  
el  valor  máximo.

Simulación  de  muchos  paseos  aleatorios  a  la  vez  Si  su  

objetivo  era  simular  muchos  paseos  aleatorios,  digamos  5000  de  ellos,  puede  generar  todos  los  
paseos  aleatorios  con  modificaciones  menores  al  código  anterior.  Si  se  pasa  una  tupla  de  2,  las  
funciones  numpy.random  generarán  una  matriz  bidimensional  de  sorteos,  y  podemos  calcular  la  suma  
acumulada  en  las  filas  para  calcular  las  5000  caminatas  aleatorias  de  una  sola  vez:

En  [258]:  ncaminatas  =  5000

En  [259]:  npasos  =  1000

En  [260]:  sorteos  =  np.random.randint(0,  2,  size=(nwalks,  nsteps))  #  0  o  1

En  [261]:  pasos  =  np.where(dibuja  >  0,  1,  ­1)

En  [262]:  caminatas  =  pasos.cumsum(1)

En  [263]:  paseos
Salida[263]:  
matriz([[ 1,  [ 1,   0,  1, ...,  8,  7,  8],  0,  ­1, ...,  34,  33,  
[ 1, 32],  0,  ­1, ...,  4,  4],  5,

...,
[ 1,  1, ...,  24,  25,  26],  2,  [ 1,  2,  3, ...,  14,  13,  
14],  [ ­1,  ­2,  ­3, ...,  ­24,  ­23,  ­22]])

Ahora,  podemos  calcular  los  valores  máximos  y  mínimos  obtenidos  en  todas  las  caminatas:

4.7  Ejemplo:  Paseos  aleatorios  |  121
Machine Translated by Google

En  [264]:  paseos.max()
Salida[264]:  138

En  [265]:  paseos.min()
Salida[265]:  ­133

De  estas  caminatas,  calculemos  el  tiempo  mínimo  de  cruce  en  30  o  –30.  Esto  es  un  poco  complicado  
porque  no  todos  los  5000  llegan  a  30.  Podemos  verificar  esto  usando  cualquier  método:

En  [266]:  hits30  =  (np.abs(walks)  >=  30).any(1)

In  [267]:  hits30  
Out[267]:  array([False,  True,  False, ...,  False,  True,  False],  dtype=bool)

In  [268]:  hits30.sum()  #  Número  que  alcanzó  30  o  ­30  Out  
[268]:  3410

Podemos  usar  esta  matriz  booleana  para  seleccionar  las  filas  de  caminatas  que  realmente  cruzan  el  
nivel  30  absoluto  y  llamar  a  argmax  a  través  del  eje  1  para  obtener  los  tiempos  de  cruce:

En  [269]:  cross_times  =  (np.abs(walks[hits30])  >=  30).argmax(1)

En  [270]:  cross_times.mean()
Salida[270]:  498.88973607038122

Siéntase  libre  de  experimentar  con  otras  distribuciones  para  los  pasos  que  no  sean  lanzamientos  de  
monedas  del  mismo  tamaño.  Solo  necesita  usar  una  función  de  generación  de  números  aleatorios  
diferente,  como  normal  para  generar  pasos  distribuidos  normalmente  con  alguna  media  y  desviación  
estándar:

En  [271]:  pasos  =  np.random.normal(loc=0,  scale=0.25,
.....: tamaño=(ncaminatas,  npasos))

4.8  Conclusión
Si  bien  gran  parte  del  resto  del  libro  se  centrará  en  desarrollar  habilidades  de  manejo  de  datos  con  
pandas,  continuaremos  trabajando  en  un  estilo  similar  basado  en  matrices.  En  el  Apéndice  A,  
profundizaremos  en  las  características  de  NumPy  para  ayudarlo  a  desarrollar  aún  más  sus  habilidades  
de  computación  de  matrices.

122  |  Capítulo  4:  Conceptos  básicos  de  NumPy:  arreglos  y  computación  vectorizada
Machine Translated by Google

CAPÍTULO  5

Primeros  pasos  con  los  pandas

los  pandas  serán  una  importante  herramienta  de  interés  a  lo  largo  de  gran  parte  del  resto  del  libro.  Contiene  
estructuras  de  datos  y  herramientas  de  manipulación  de  datos  diseñadas  para  hacer  que  la  limpieza  y  el  análisis  
de  datos  sean  rápidos  y  fáciles  en  Python.  pandas  a  menudo  se  usa  junto  con  herramientas  de  computación  
numérica  como  NumPy  y  SciPy,  bibliotecas  analíticas  como  statsmodels  y  scikit­learn,  y  bibliotecas  de  
visualización  de  datos  como  matplotlib.  pandas  adopta  partes  significativas  del  estilo  idiomático  de  computación  
basada  en  arreglos  de  NumPy,  especialmente  funciones  basadas  en  arreglos  y  una  preferencia  por  el  
procesamiento  de  datos  sin  bucles  for .

Si  bien  pandas  adopta  muchos  lenguajes  de  codificación  de  NumPy,  la  mayor  diferencia  es  que  pandas  está  
diseñado  para  trabajar  con  datos  tabulares  o  heterogéneos.  NumPy,  por  el  contrario,  es  más  adecuado  para  
trabajar  con  datos  de  matrices  numéricas  homogéneas.

Desde  que  se  convirtió  en  un  proyecto  de  código  abierto  en  2010,  pandas  ha  madurado  hasta  convertirse  en  
una  biblioteca  bastante  grande  que  se  puede  aplicar  en  un  amplio  conjunto  de  casos  de  uso  del  mundo  real.  La  
comunidad  de  desarrolladores  ha  crecido  a  más  de  800  colaboradores  distintos,  que  han  estado  ayudando  a  
construir  el  proyecto  a  medida  que  lo  utilizan  para  resolver  sus  problemas  de  datos  cotidianos.

A  lo  largo  del  resto  del  libro,  utilizo  la  siguiente  convención  de  importación  para  pandas:

En  [1]:  importar  pandas  como  pd

Por  lo  tanto,  cada  vez  que  vea  pd.  en  código,  se  refiere  a  pandas.  También  puede  resultarle  más  fácil  importar  
Series  y  DataFrame  al  espacio  de  nombres  local,  ya  que  se  utilizan  con  mucha  frecuencia:

En  [2]:  from  pandas  import  Series,  DataFrame

123
Machine Translated by Google

5.1  Introducción  a  las  estructuras  de  datos  pandas
Para  comenzar  con  pandas,  deberá  familiarizarse  con  sus  dos  estructuras  de  datos  de  caballo  de  
batalla:  Series  y  DataFrame.  Si  bien  no  son  una  solución  universal  para  todos  los  problemas,  brindan  
una  base  sólida  y  fácil  de  usar  para  la  mayoría  de  las  aplicaciones.

Serie
Una  serie  es  un  objeto  similar  a  una  matriz  unidimensional  que  contiene  una  secuencia  de  valores  
(de  tipos  similares  a  los  tipos  NumPy)  y  una  matriz  asociada  de  etiquetas  de  datos,  denominada  índice.
La  serie  más  simple  se  forma  solo  a  partir  de  una  matriz  de  datos:

En  [11]:  obj  =  pd.Series([4,  7,  ­5,  3])

En  [12]:  objeto
Salida[12]:  
0  4
1 7
2 ­5
3  3  
dtipo:  int64

La  representación  de  cadena  de  una  serie  que  se  muestra  de  forma  interactiva  muestra  el  índice  a  
la  izquierda  y  los  valores  a  la  derecha.  Dado  que  no  especificamos  un  índice  para  los  datos,  se  crea  
uno  predeterminado  que  consta  de  los  números  enteros  de  0  a  N  ­  1  (donde  N  es  la  longitud  de  los  
datos).  Puede  obtener  la  representación  de  matriz  y  el  objeto  de  índice  de  la  Serie  a  través  de  sus  
valores  y  atributos  de  índice,  respectivamente:

In  [13]:  obj.values  Out[13]:  
array([ 4,  7,  ­5,  3])

En  [14]:  obj.index  #  como  rango  (4)
Salida[14]:  RangeIndex(inicio=0,  parada=4,  paso=1)

A  menudo,  será  deseable  crear  una  serie  con  un  índice  que  identifique  cada  punto  de  datos  con  una  
etiqueta:

En  [15]:  obj2  =  pd.Series([4,  7,  ­5,  3],  index=['d',  'b',  'a',  'c'])

En  [16]:  obj2
Salida[16]:  base  de  

4
datos 7
a ­5
C 3
tipo:  int64

In  [17]:  obj2.index  Out[17]:  
Index(['d',  'b',  'a',  'c'],  dtype='objeto')

124  |  Capítulo  5:  Primeros  pasos  con  los  pandas
Machine Translated by Google

En  comparación  con  las  matrices  NumPy,  puede  usar  etiquetas  en  el  índice  al  seleccionar  solo
valores  o  un  conjunto  de  valores:

En  [18]:  obj2['a']
Salida[18]:  ­5

En  [19]:  obj2['d']  =  6

En  [20]:  obj2[['c',  'a',  'd']]
Fuera[20]:
C 3
a ­5
6
d  tipo:  int64

Aquí  ['c',  'a',  'd']  se  interpreta  como  una  lista  de  índices,  aunque  contiene
cadenas  en  lugar  de  enteros.

Usar  funciones  NumPy  u  operaciones  similares  a  NumPy,  como  filtrar  con  un  valor  booleano
matriz,  la  multiplicación  escalar  o  la  aplicación  de  funciones  matemáticas,  conservará  el  valor  del  índice
enlace:

En  [21]:  obj2[obj2  >  0]
Fuera[21]:
6  _

b 7
C 3
tipo:  int64

En  [22]:  obj2  *  2
Fuera[22]:
d  12
b 14
a ­10
C 6
tipo:  int64

En  [23]:  np.exp(obj2)
Fuera[23]:
d 403.428793
b 1096.633158
a 0.006738
C 20.085537
tipo:  float64

Otra  forma  de  pensar  en  una  serie  es  como  un  dictado  ordenado  de  longitud  fija,  ya  que  es  un  mapa.
ping  de  valores  de  índice  a  valores  de  datos.  Se  puede  utilizar  en  muchos  contextos  en  los  que  podría
usa  un  dictado:

En  [24]:  'b'  en  obj2
Salida[24]:  Verdadero

5.1  Introducción  a  las  estructuras  de  datos  pandas  |  125
Machine Translated by Google

En  [25]:  'e'  en  obj2
Salida[25]:  Falso

Si  tiene  datos  contenidos  en  un  dictado  de  Python,  puede  crear  una  Serie  a  partir  de  él
pasando  el  dictado:

En  [26]:  sdata  =  {'Ohio':  35000,  'Texas':  71000,  'Oregón':  16000,  'Utah':  5000}

En  [27]:  obj3  =  pd.Series(sdata)

En  [28]:  obj3
Fuera[28]:
Ohio 35000
Oregón 16000
Texas 71000
Utah 5000
tipo:  int64

Cuando  solo  está  pasando  un  dict,  el  índice  en  la  serie  resultante  tendrá  el  dict
llaves  en  orden  ordenado.  Puede  anular  esto  pasando  las  teclas  de  dictado  en  el  orden  que
desea  que  aparezcan  en  la  Serie  resultante:

En  [29]:  estados  =  ['California',  'Ohio',  'Oregón',  'Texas']

En  [30]:  obj4  =  pd.Series(sdata,  index=states)

En  [31]:  obj4
Fuera[31]:
California Yaya
Ohio 35000.0
Oregón 16000.0
Texas 71000.0
tipo:  float64

Aquí,  tres  valores  encontrados  en  sdata  se  colocaron  en  las  ubicaciones  apropiadas,  pero  dado  que
no  se  encontró  ningún  valor  para  'California' ,  aparece  como  NaN  (no  es  un  número),  lo  cual  es
sidered  en  pandas  para  marcar  los  valores  que  faltan  o  NA.  Dado  que  'Utah'  no  se  incluyó  en
estados,  se  excluye  del  objeto  resultante.

Usaré  los  términos  "faltante"  o  "NA"  indistintamente  para  referirme  a  los  datos  faltantes.  El
Las  funciones  isnull  y  notnull  en  pandas  deben  usarse  para  detectar  datos  faltantes:

En  [32]:  pd.isnull(obj4)
Fuera[32]:
california  ohio Verdadero

FALSO
Oregón FALSO
Texas FALSO
dtipo:  booleano

En  [33]:  pd.notnull(obj4)
Fuera[33]:

126  |  Capítulo  5:  Primeros  pasos  con  los  pandas
Machine Translated by Google

California FALSO
Ohio Verdadero

Oregón Verdadero

Texas   Verdadero

dtype:  bool

Series  también  tiene  estos  como  métodos  de  instancia:

En  [34]:  obj4.isnull()
Fuera[34]:
california  ohio Verdadero

FALSO
Oregón FALSO
Texas   FALSO
dtype:  bool

Discuto  el  trabajo  con  datos  faltantes  con  más  detalle  en  el  Capítulo  7.

Una  función  de  Series  útil  para  muchas  aplicaciones  es  que  se  alinea  automáticamente  por  índice
etiqueta  en  operaciones  aritméticas:

En  [35]:  obj3
Fuera[35]:
Ohio 35000
Oregón 16000
Tejas  71000
Utah  5000
tipo:  int64

En  [36]:  obj4
Fuera[36]:
California Yaya
Ohio 35000.0
Oregón 16000.0
Texas 71000.0
tipo:  float64

En  [37]:  obj3  +  obj4
Fuera[37]:
California Yaya
Ohio 70000.0
Oregón 32000.0
Texas 142000.0
Utah   Yaya

dtype:  float64

Las  funciones  de  alineación  de  datos  se  abordarán  con  más  detalle  más  adelante.  si  tienes  experiencia
con  las  bases  de  datos,  puede  pensar  en  esto  como  algo  similar  a  una  operación  de  combinación.

Tanto  el  objeto  Serie  en  sí  como  su  índice  tienen  un  atributo  de  nombre ,  que  se  integra  con
otras  áreas  clave  de  la  funcionalidad  de  pandas:

5.1  Introducción  a  las  estructuras  de  datos  pandas  |  127
Machine Translated by Google

En  [38]:  obj4.name  =  'población'

En  [39]:  obj4.index.name  =  'estado'

En  [40]:  obj4
Fuera[40]:
estado
California Yaya
Ohio 35000.0
Oregón 16000.0
Texas 71000.0
Nombre:  población,  dtype:  float64

El  índice  de  una  serie  se  puede  modificar  en  el  lugar  mediante  asignación:

En  [41]:  objeto
Fuera[41]:
0  4
1   7
2 ­5
3 3
tipo:  int64

En  [42]:  obj.index  =  ['Bob',  'Steve',  'Jeff',  'Ryan']

En  [43]:  objeto
Fuera[43]:
Beto 4
Steve 7
jeff ­5
Tipo   3
de  Ryan:  int64

Marco  de  datos

Un  DataFrame  representa  una  tabla  rectangular  de  datos  y  contiene  una  colección  ordenada.
ción  de  columnas,  cada  una  de  las  cuales  puede  ser  un  tipo  de  valor  diferente  (numérico,  cadena,
booleano,  etc).  El  DataFrame  tiene  un  índice  de  fila  y  columna;  se  puede  pensar  en
como  un  dictado  de  Serie,  todos  comparten  el  mismo  índice.  Debajo  del  capó,  los  datos  se  almacenan  como  uno
o  más  bloques  bidimensionales  en  lugar  de  una  lista,  dictado  o  alguna  otra  colección  de

matrices  unidimensionales.  Los  detalles  exactos  de  las  partes  internas  de  DataFrame  están  fuera  del
alcance  de  este  libro.

Si  bien  un  DataFrame  es  físicamente  bidimensional,  puede  usarlo  para
representar  datos  dimensionales  superiores  en  un  formato  tabular  usando
indexación  archivística,  un  tema  que  discutiremos  en  el  Capítulo  8  y  un
ingrediente  en  algunas  de  las  características  más  avanzadas  de  manejo  de  datos  en
pandas

128  |  Capítulo  5:  Primeros  pasos  con  los  pandas
Machine Translated by Google

Hay  muchas  formas  de  construir  un  DataFrame,  aunque  una  de  las  más  comunes  es  a  partir  de  listas  
de  igual  longitud  o  matrices  NumPy:

datos  =  {'estado':  ['Ohio',  'Ohio',  'Ohio',  'Nevada',  'Nevada',  'Nevada'],
'año':  [2000,  2001,  2002,  2001,  2002,  2003],  'pop':  [1.5,  1.7,  
3.6,  2.4,  2.9,  3.2]}
marco  =  pd.DataFrame(datos)

El  DataFrame  resultante  tendrá  su  índice  asignado  automáticamente  como  con  Series,  y  las  columnas  
se  colocarán  en  orden:

Entrada  [45]:  marco  
Salida[45]:  
estado  pop  año  0  1,5  
Ohio  2000  1  1,7  Ohio  2001  
2  3,6  Ohio  2002  3  2,4  
Nevada  2001

4  2.9  Nevada  2002  5  3.2  
Nevada  2003

Si  está  utilizando  el  cuaderno  Jupyter,  los  objetos  Pandas  DataFrame  se  mostrarán  como  una  tabla  
HTML  más  amigable  para  el  navegador.

Para  DataFrames  grandes,  el  método  head  selecciona  solo  las  primeras  cinco  filas:

En  [46]:  cuadro.cabeza()
Salida[46]:  
estado  pop  año  0  1,5  
Ohio  2000  1  1,7  Ohio  2001  
2  3,6  Ohio  2002

3  2.4  Nevada  2001  4  2.9  
Nevada  2002

Si  especifica  una  secuencia  de  columnas,  las  columnas  de  DataFrame  se  organizarán  en  ese  orden:

En  [47]:  pd.DataFrame(datos,  columnas=['año',  'estado',  'pop'])
Out[47]:  
año  población  estatal  0  
2000  Ohio  1,5  1  2001  Ohio  
1,7  2  2002  Ohio  3,6  3  2001  
Nevada  2,4

4  2002  Nevada  2,9  5  2003  
Nevada  3,2

Si  pasa  una  columna  que  no  está  contenida  en  el  dict,  aparecerá  con  valores  faltantes  en  el  resultado:

En  [48]:  cuadro2  =  pd.DataFrame(datos,  columnas=['año',  'estado',  'población',  'deuda'],  índice=['uno',  'dos',  
.....: 'tres',  'cuatro  ',  'cinco',  'seis'])
.....:

5.1  Introducción  a  las  estructuras  de  datos  pandas  |  129
Machine Translated by Google

En  [49]:  fotograma2
Fuera[49]:
año  deuda  pop  estatal
uno 2000  Ohio  1,5  NaN
dos   2001  Ohio  1,7  NaN
tres  2002  Ohio  3,6  NaN
cuatro  2001  Nevada  2.4  NaN
cinco  2002  Nevada  2.9  NaN
seis  2003  Nevada  3.2  NaN

En  [50]:  frame2.columns
Salida[50]:  Índice(['año',  'estado',  'pop',  'deuda'],  dtype='objeto')

Una  columna  en  un  DataFrame  se  puede  recuperar  como  una  Serie  ya  sea  por  notación  similar  a  dict  o
por  atributo:

En  [51]:  frame2['estado']
Fuera[51]:
uno Ohio
dos Ohio
tres Ohio
cuatro Nevada
cinco Nevada
seis Nevada
Nombre:  estado,  dtype:  objeto

En  [52]:  cuadro2.año
Fuera[52]:
uno 2000
dos 2001
tres   2002
cuatro   2001
cinco   2002
seis 2003
Nombre:  año,  dtype:  int64

Acceso  similar  a  un  atributo  (p.  ej.,  marco2.año)  y  finalización  con  pestañas  de  la  columna.
umn  nombres  en  IPython  se  proporciona  para  su  comodidad.

frame2[column]  funciona  para  cualquier  nombre  de  columna,  pero  frame2.column
solo  funciona  cuando  el  nombre  de  la  columna  es  una  variable  de  Python  válida
nombre.

Tenga  en  cuenta  que  la  Serie  devuelta  tiene  el  mismo  índice  que  el  DataFrame  y  su  nombre
El  atributo  se  ha  establecido  correctamente.

Las  filas  también  se  pueden  recuperar  por  posición  o  nombre  con  el  atributo  especial  loc  (muy
más  sobre  esto  más  adelante):

130  |  Capítulo  5:  Primeros  pasos  con  los  pandas
Machine Translated by Google

En  [53]:  frame2.loc['tres']
Fuera[53]:
estado   2002
del  año Ohio
deuda   3.6
popular Yaya

Nombre:  tres,  dtype:  objeto

Las  columnas  se  pueden  modificar  por  asignación.  Por  ejemplo,  la  columna  vacía  'deuda'
se  le  podría  asignar  un  valor  escalar  o  una  matriz  de  valores:

En  [54]:  frame2['deuda']  =  16.5

En  [55]:  fotograma2
Fuera[55]:
año  deuda  pop  estatal
uno 2000  Ohio  1,5  16,5
dos 2001  Ohio  1,7  16,5
tres  2002  Ohio  3,6  16,5
cuatro  2001  Nevada  2.4  16.5
cinco  2002  Nevada  2.9  16.5
seis  2003  Nevada  3.2  16.5

En  [56]:  frame2['deuda']  =  np.arange(6.)

En  [57]:  fotograma2
Fuera[57]:
año  deuda  pop  estatal
uno 2000  Ohio  1,5  0,0
dos   2001  Ohio  1,7  1,0
tres  2002  Ohio  3,6  2,0
cuatro  2001  Nevada  2.4  3.0
cinco  2002  Nevada  2.9  4.0
seis 2003  Nevada  3,2  5,0

Cuando  está  asignando  listas  o  matrices  a  una  columna,  la  longitud  del  valor  debe  coincidir  con  la
longitud  de  la  trama  de  datos.  Si  asigna  una  serie,  sus  etiquetas  se  realinearán  exactamente  para
el  índice  de  DataFrame,  insertando  valores  faltantes  en  cualquier  agujero:

En  [58]:  val  =  pd.Series([­1.2,  ­1.5,  ­1.7],  index=['dos',  'cuatro',  'cinco'])

En  [59]:  frame2['deuda']  =  val

En  [60]:  fotograma2
Fuera[60]:
año  deuda  pop  estatal
uno 2000  Ohio  1,5  NaN
dos   2001  Ohio  1,7  ­1,2
tres  2002  Ohio  3,6  NaN
cuatro  2001  Nevada  2.4  ­1.5
cinco  2002  Nevada  2.9  ­1.7
seis  2003  Nevada  3.2  NaN

5.1  Introducción  a  las  estructuras  de  datos  pandas  |  131
Machine Translated by Google

Asignar  una  columna  que  no  existe  creará  una  nueva  columna.  La  palabra  clave  del
eliminar  columnas  como  con  un  dict.

Como  ejemplo  de  del,  primero  agrego  una  nueva  columna  de  valores  booleanos  donde  el  estado
columna  es  igual  a  'Ohio':

En  [61]:  cuadro2['oriental']  =  cuadro2.estado  ==  'Ohio'

En  [62]:  fotograma2
Fuera[62]:
año  deuda  pop  estatal  del  este
uno 2000  Ohio  1,5  NaN  Verdadero
dos 2001  Ohio  1,7  ­1,2 Verdadero

tres  2002  Ohio  3,6  NaN  cuatro  2001  Nevada   Verdadero

2,4  ­1,5  cinco  2002  Nevada  2,9  ­1,7  seis  2003   FALSO
Nevada  3,2  NaN FALSO
FALSO

No  se  pueden  crear  columnas  nuevas  con  la  sintaxis  frame2.eastern .

El  método  del  se  puede  usar  para  eliminar  esta  columna:

En  [63]:  del  frame2['oriental']

En  [64]:  marco2.columnas
Salida[64]:  Índice(['año',  'estado',  'pop',  'deuda'],  dtype='objeto')

La  columna  devuelta  de  indexar  un  DataFrame  es  una  vista  en  el
datos  subyacentes,  no  una  copia.  Por  lo  tanto,  cualquier  modificación  in  situ  al
La  serie  se  reflejará  en  el  DataFrame.  La  columna  puede  ser
copiado  explícitamente  con  el  método  de  copia  de  la  Serie .

Otra  forma  común  de  datos  es  un  dictado  anidado  de  dictados:

En  [65]:  población  =  {'Nevada':  {2001:  2.4,  2002:  2.9},
.....: 'Ohio':  {2000:  1,5,  2001:  1,7,  2002:  3,6}}

Si  el  dict  anidado  se  pasa  al  DataFrame,  los  pandas  interpretarán  las  claves  del  dict  externo
como  las  columnas  y  las  claves  internas  como  los  índices  de  las  filas:

En  [66]:  frame3  =  pd.DataFrame(pop)

En  [67]:  fotograma3
Fuera[67]:
nevada  ohio
2000 NaN  1,5

132  |  Capítulo  5:  Primeros  pasos  con  los  pandas
Machine Translated by Google

2001 2.4  1.7
2002 2,9  3,6

Puede  transponer  el  DataFrame  (intercambiar  filas  y  columnas)  con  una  sintaxis  similar  a  un
matriz  NumPy:

En  [68]:  cuadro3.T
Fuera[68]:
2000  2001  2002
Nevada  NaN  2,4  2,9
Ohio 1,5  1,7  3,6

Las  claves  en  los  dictados  internos  se  combinan  y  ordenan  para  formar  el  índice  en  el  resultado.
Esto  no  es  cierto  si  se  especifica  un  índice  explícito:

En  [69]:  pd.DataFrame(pop,  índice=[2001,  2002,  2003])
Fuera[69]:
nevada  ohio
2001 2.4  1.7
2002   2,9  3,6
2003 NaN  NaN

Los  dictados  de  series  se  tratan  de  la  misma  manera:

En  [70]:  pdata  =  {'Ohio':  frame3['Ohio'][:­1],
.....: 'Nevada':  frame3['Nevada'][:2]}

En  [71]:  pd.DataFrame(pdata)
Fuera[71]:
nevada  ohio
2000 NaN  1,5
2001 2.4  1.7

Para  obtener  una  lista  completa  de  las  cosas  que  puede  pasar  al  constructor  de  DataFrame,  consulte  la  Tabla  5­1.

Si  el  índice  y  las  columnas  de  un  DataFrame  tienen  sus  atributos  de  nombre  establecidos,  estos  también  serán

desplegado:

En  [72]:  frame3.index.name  =  'año';  frame3.columns.name  =  'estado'

En  [73]:  fotograma3
Fuera[73]:
estado  Nevada  Ohio
año  
2000 NaN  1,5
2001 2.4  1.7
2002 2,9  3,6

Al  igual  que  con  Series,  el  atributo  de  valores  devuelve  los  datos  contenidos  en  el  DataFrame  como  un

ndarray  bidimensional:

En  [74]:  frame3.values
Fuera[74]:
matriz([[ nan,  1.5],

5.1  Introducción  a  las  estructuras  de  datos  pandas  |  133
Machine Translated by Google

[ 2.4,  1.7],  [ 2.9,  
3.6]])

Si  las  columnas  de  DataFrame  son  de  diferentes  tipos,  se  elegirá  el  tipo  de  la  matriz  de  valores  para  
acomodar  todas  las  columnas:

In  [75]:  frame2.values  Out[75]:  
array([[2000,  
'Ohio',  1.5,  nan],  [2001,  'Ohio',  1.7,  ­1.2],  
[2002,  'Ohio',  3.6,  nan],  [2001,  
'Nevada',  2.4,  ­1.5],  [2002,  'Nevada',  
2.9,  ­1.7],  [2003,  'Nevada',  3.2,  nan]],  
dtype=objeto)

Tabla  5­1.  Posibles  entradas  de  datos  al  constructor  de  DataFrame

Tipo notas

Dict  2D  ndarray   Una  matriz  de  datos,  pasando  etiquetas  de  fila  y  columna  opcionales

de  matrices,  listas  o  tuplas Cada  secuencia  se  convierte  en  una  columna  en  el  DataFrame;  todas  las  secuencias  deben  tener  la  misma  longitud

NumPy  matriz  estructurada/registro Tratado  como  el  caso  "dict  of  arrays"

dictado  de  la  serie Cada  valor  se  convierte  en  una  columna;  los  índices  de  cada  serie  se  unen  para  formar  el  índice  de  fila  del  resultado  si  no  se  

pasa  un  índice  explícito

dictado  de  dictados Cada  dictado  interno  se  convierte  en  una  columna;  las  claves  se  unen  para  formar  el  índice  de  fila  como  en  el  "dict  of

Caso  de  la  serie

Lista  de  dictados  o  Serie Cada  elemento  se  convierte  en  una  fila  en  el  DataFrame;  unión  de  claves  dict  o  índices  de  serie  se  convierten  en  el

Etiquetas  de  columna  de  DataFrame

Lista  de  listas  o  tuplas Tratado  como  el  caso  "2D  ndarray"

Otro  marco  de  datos Los  índices  de  DataFrame  se  utilizan  a  menos  que  se  pasen  otros  diferentes

Matriz  enmascarada  NumPy Como  el  caso  "2D  ndarray",  excepto  que  los  valores  enmascarados  se  vuelven  NA/faltan  en  el  resultado  de  DataFrame

Objetos  de  índice  

Los  objetos  de  índice  de  pandas  son  responsables  de  contener  las  etiquetas  de  los  ejes  y  otros  metadatos  
(como  el  nombre  o  los  nombres  de  los  ejes).  Cualquier  matriz  u  otra  secuencia  de  etiquetas  que  utilice  al  
construir  una  Serie  o  Marco  de  datos  se  convierte  internamente  en  un  Índice:

En  [76]:  obj  =  pd.Series(rango(3),  índice=['a',  'b',  'c'])

En  [77]:  índice  =  obj.index

En  [78]:  índice
Salida[78]:  Índice(['a',  'b',  'c'],  dtype='objeto')

En  [79]:  índice[1:]
Salida[79]:  Índice(['b',  'c'],  dtype='objeto')

Los  objetos  de  índice  son  inmutables  y,  por  lo  tanto,  el  usuario  no  puede  modificarlos:

134  |  Capítulo  5:  Primeros  pasos  con  los  pandas
Machine Translated by Google

índice[1]  =  'd'  #  TypeError

La  inmutabilidad  hace  que  sea  más  seguro  compartir  objetos  de  índice  entre  estructuras  de  datos:

En  [80]:  etiquetas  =  pd.Index(np.arange(3))

En  [81]:  etiquetas
Salida[81]:  Int64Index([0,  1,  2],  dtype='int64')

En  [82]:  obj2  =  pd.Series([1.5,  ­2.5,  0],  index=etiquetas)

Entrada  [83]:  
obj2  
Salida  
[83]:  0  1.5  1  ­2.5
2 0.0
tipo:  float64

In  [84]:  obj2.index  es  etiquetas  Out[84]:  True

Algunos  usuarios  no  suelen  aprovechar  las  capacidades  proporcionadas  
por  los  índices,  pero  debido  a  que  algunas  operaciones  arrojarán  
resultados  que  contienen  datos  indexados,  es  importante  comprender  
cómo  funcionan.

Además  de  ser  similar  a  una  matriz,  un  índice  también  se  comporta  como  un  conjunto  de  tamaño  fijo:

En  [85]:  fotograma3
Salida[85]:  
estado  Nevada  Ohio
año  
2000 NaN  1,5
2001 2.4  1.7
2002 2,9  3,6

In  [86]:  frame3.columns  Out[86]:  
Index(['Nevada',  'Ohio'],  dtype='object',  name='state')

In  [87]:  'Ohio'  en  frame3.columns  Out  [87]:  
Verdadero

En  [88]:  2003  en  frame3.index  Salida  [88]:  
Falso

A  diferencia  de  los  conjuntos  de  Python,  un  índice  de  pandas  puede  contener  etiquetas  duplicadas:

En  [89]:  dup_labels  =  pd.Index(['foo',  'foo',  'bar',  'bar'])

En  [90]:  dup_labels
Salida[90]:  Índice(['foo',  'foo',  'bar',  'bar'],  dtype='objeto')

5.1  Introducción  a  las  estructuras  de  datos  pandas  |  135
Machine Translated by Google

Las  selecciones  con  etiquetas  duplicadas  seleccionarán  todas  las  apariciones  de  esa  etiqueta.

Cada  índice  tiene  una  serie  de  métodos  y  propiedades  para  establecer  la  lógica,  que  responden  a  otras  preguntas  
comunes  sobre  los  datos  que  contiene.  Algunos  útiles  se  resumen  en  la  Tabla  5­2.

Tabla  5­2.  Algunos  métodos  y  propiedades  de  Index
Método Descripción

adjuntar Concatenar  con  objetos  de  índice  adicionales,  produciendo  un  nuevo  índice

diferencia Calcule  la  diferencia  de  conjuntos  como  un  índice

intersección  Calcular  conjunto  de  intersección

Unión Unión  de  conjuntos  de  cómputos

es  en Calcule  la  matriz  booleana  que  indica  si  cada  valor  está  contenido  en  la  colección  pasada

borrar Calcule  el  nuevo  índice  con  el  elemento  en  el  índice  que  eliminé

gota Calcule  el  nuevo  índice  eliminando  los  valores  pasados

insertar Calcule  el  nuevo  índice  insertando  el  elemento  en  el  índice  i

is_monotonic  Devuelve  True  si  cada  elemento  es  mayor  o  igual  que  el  elemento  
anterior  is_unique Devuelve  True  si  el  índice  no  tiene  valores  duplicados
único Calcule  la  matriz  de  valores  únicos  en  el  índice

5.2  Funcionalidad  esencial
Esta  sección  lo  guiará  a  través  de  la  mecánica  fundamental  de  la  interacción  con  los  datos  contenidos  en  una  serie  
o  marco  de  datos.  En  los  próximos  capítulos,  profundizaremos  en  los  temas  de  análisis  y  manipulación  de  datos  
utilizando  pandas.  Este  libro  no  pretende  servir  como  documentación  exhaustiva  para  la  biblioteca  de  pandas;  en  su  
lugar,  nos  centraremos  en  las  características  más  importantes,  dejando  las  cosas  menos  comunes  (es  decir,  más  
esotéricas)  para  que  las  explore  por  su  cuenta.

Reindexación  Un  

método  importante  en  los  objetos  pandas  es  la  reindexación,  lo  que  significa  crear  un  nuevo  objeto  con  los  datos  
conformes  a  un  nuevo  índice.  Considere  un  ejemplo:

En  [91]:  obj  =  pd.Series([4.5,  7.2,  ­5.3,  3.6],  index=['d',  'b',  'a',  'c'])

Entrada  [92]:  obj  
Salida[92]:  
d  4.5  b
7.2
un  ­5.3
3.6  
c  dtipo:  float64

136  |  Capítulo  5:  Primeros  pasos  con  los  pandas
Machine Translated by Google

Llamar  a  reindex  en  esta  serie  reorganiza  los  datos  de  acuerdo  con  el  nuevo  índice,  introduciendo  
valores  faltantes  si  alguno  de  los  valores  del  índice  no  estaba  ya  presente:

En  [93]:  obj2  =  obj.reindex(['a',  'b',  'c',  'd',  'e'])

Entrada  [94]:  obj2  
Salida  
[94]:  a  ­5.3  
b  7.2
C 3.6
d 4.5
mi Yaya

tipo:  float64

Para  datos  ordenados  como  series  de  tiempo,  puede  ser  deseable  hacer  alguna  interpolación  o  
completar  valores  al  reindexar.  La  opción  de  método  nos  permite  hacer  esto,  usando  un  método  
como  ffill,  que  reenvía  los  valores:

En  [95]:  obj3  =  pd.Series(['azul',  'morado',  'amarillo'],  index=[0,  2,  4])

En  [96]:  obj3
Out[96]:  0  
azul  2  púrpura  
amarillo  dtype:  
4 objeto

En  [97]:  obj3.reindex(rango(6),  método='relleno')
Out[97]:  
azul  0  
1
azul  violeta  
2 violeta  
amarillo  
3  4 amarillo  
5 dtype:  
objeto

Con  DataFrame,  la  reindexación  puede  modificar  el  índice  (fila),  las  columnas  o  ambos.  Cuando  se  
pasa  solo  una  secuencia,  vuelve  a  indexar  las  filas  en  el  resultado:

En  [98]:  marco  =  pd.DataFrame(np.arange(9).reshape((3,  3)),  índice=['a',  'c',  'd'],  
.....: columnas=['Ohio',  'Texas',  
.....: 'California'])

En  [99]:  cuadro
Fuera[99]:
Ohio  Texas  California
a 0   1 2
C 3 4 5  

d 6 7 8

En  [100]:  cuadro2  =  cuadro.reindex(['a',  'b',  'c',  'd'])

5.2  Funcionalidad  esencial  |  137
Machine Translated by Google

En  [101]:  fotograma2
Fuera[101]:
Ohio  Texas  California
a  0.0  b   1,0   2,0  
NaN NaN NaN
c  3.0  d  6.0 4.0 5.0
7.0 8.0

Las  columnas  se  pueden  volver  a  indexar  con  la  palabra  clave  de  columnas :

En  [102]:  estados  =  ['Texas',  'Utah',  'California']

En  [103]:  frame.reindex(columnas=estados)
Fuera[103]:  
Texas  Utah  California
a 1  NaN 2
C 4  NaN 5
d 7  NaN 8

Consulte  la  Tabla  5­3  para  obtener  más  información  sobre  los  argumentos  para  reindexar.

Como  exploraremos  con  más  detalle,  puede  reindexar  de  manera  más  sucinta  mediante  la  indexación  de  
etiquetas  con  loc,  y  muchos  usuarios  prefieren  usarlo  exclusivamente:

En  [104]:  frame.loc[['a',  'b',  'c',  'd'],  estados]
Salida[104]:  
Texas  Utah  California  1.0  NaN  2.0
a
b NaN  NaN Yaya
C 4,0  NaN 5.0
d 7,0  NaN 8.0

Tabla  5­3.  Argumentos  de  función  de  reindexación

Argumento Descripción  

índice Nueva  secuencia  para  usar  como  índice.  Puede  ser  una  instancia  de  índice  o  cualquier  otra  estructura  de  datos  de  Python  similar  a  una  secuencia.  Un  

índice  se  utilizará  exactamente  como  está  sin  ninguna  copia.

método Método  de  interpolación  (relleno);  'ffill'  se  llena  hacia  adelante,  mientras  que  'bfill'  se  llena  hacia  atrás.

fill_value  Valor  de  sustitución  que  se  utilizará  al  introducir  datos  faltantes  mediante  la  reindexación.

límite Cuando  se  rellene  hacia  delante  o  hacia  atrás,  espacio  de  tamaño  máximo  (en  número  de  elementos)  a  rellenar.

Tolerancia  Cuando  se  rellene  hacia  delante  o  hacia  atrás,  la  brecha  de  tamaño  máximo  (en  distancia  numérica  absoluta)  para  rellenar  las  coincidencias  inexactas.

nivel Haga  coincidir  el  índice  simple  en  el  nivel  de  MultiIndex;  de  lo  contrario,  seleccione  un  subconjunto  de.

Copiar Si  es  Verdadero,  copie  siempre  los  datos  subyacentes  incluso  si  el  índice  nuevo  es  equivalente  al  índice  anterior;  si  es  False,  no  copie  los  datos  cuando  

los  índices  sean  equivalentes.

Eliminar  entradas  de  un  eje  Eliminar  una  o  más  

entradas  de  un  eje  es  fácil  si  ya  tiene  una  matriz  o  lista  de  índice  sin  esas  entradas.  Como  eso  puede  requerir  un  
poco  de  munging  y  lógica  establecida,  el

138  |  Capítulo  5:  Primeros  pasos  con  los  pandas
Machine Translated by Google

El  método  drop  devolverá  un  nuevo  objeto  con  el  valor  o  valores  indicados  eliminados  
de  un  eje:

En  [105]:  obj  =  pd.Series(np.arange(5.),  index=['a',  'b',  'c',  'd',  'e'])

Entrada  [106]:  obj  
Salida  [106]:  
un   0.0  
segundo 1.0
C 2.0
d 3.0
mi 4.0
tipo:  float64

En  [107]:  nuevo_obj  =  obj.drop('c')

Entrada  [108]:  nuevo_obj  
Salida  [108]:  
a 0.0
1.0
bd 3.0
mi 4.0
tipo:  float64

En  [109]:  obj.drop(['d',  'c'])
Salida[109]:  
a 0.0
b 1.0  
mi 4.0  
dtipo:  float64

Con  DataFrame,  los  valores  de  índice  se  pueden  eliminar  de  cualquiera  de  los  ejes.  Para  ilustrar  esto,  
primero  creamos  un  DataFrame  de  ejemplo:

En  [110]:  data  =  pd.DataFrame(np.arange(16).reshape((4,  4)),  index=['Ohio',  
.....: 'Colorado',  'Utah',  'New  York'],  column=  ['uno',  'dos',  'tres',  'cuatro'])
.....:

En  [111]:  datos
Fuera[111]:
uno  dos  tres  cuatro  3
Ohio 0 1 2
Colorado 4 5 6 7
Utah  9 8 10 11
Nueva  York  12  13 14 15

Al  llamar  a  drop  con  una  secuencia  de  etiquetas,  se  eliminarán  los  valores  de  las  etiquetas  de  fila  (eje  0):

En  [112]:  data.drop(['Colorado',  'Ohio'])
Fuera[112]:
uno  dos  tres  CUATRO
Utah 8 9 10 11
Nueva  York  12  13 14 15

5.2  Funcionalidad  esencial  |  139
Machine Translated by Google

Puede  eliminar  valores  de  las  columnas  pasando  axis=1  o  axis='columns':

En  [113]:  data.drop('dos',  eje=1)
Fuera[113]:
uno  tres  cuatro  3
Ohio 0 2
Colorado 4 6 7
Utah  8 10 11
Nueva  York  12 14 15

En  [114]:  data.drop(['dos',  'cuatro'],  eje='columnas')
Fuera[114]:
uno  tres
Ohio 0 2
Colorado 4
Utah  8 6  10
Nueva  York  12 14

Muchas  funciones,  como  soltar,  que  modifican  el  tamaño  o  la  forma  de  una  serie  o  un  marco  de  datos,  pueden  
manipular  un  objeto  en  el  lugar  sin  devolver  un  objeto  nuevo:

En  [115]:  obj.drop('c',  inplace=True)

Entrada  [116]:  obj  
Salida  [116]:  
a 0.0
1.0
bd 3.0
mi 4.0
tipo:  float64

Tenga  cuidado  con  el  inplace,  ya  que  destruye  cualquier  dato  que  se  suelte.

Indexación,  selección  y  filtrado
La  indexación  de  series  (obj[...])  funciona  de  manera  análoga  a  la  indexación  de  matrices  NumPy,  excepto  
que  puede  usar  los  valores  de  índice  de  Series  en  lugar  de  solo  números  enteros.  Aquí  hay  algunos  ejemplos  
de  esto:

En  [117]:  obj  =  pd.Series(np.arange(4.),  index=['a',  'b',  'c',  'd'])

Entrada  [118]:  obj  
Salida  [118]:  
a 0.0
b 1.0
C 2.0
d  3.0  dtipo:  
float64

En  [119]:  objeto['b']
Salida[119]:  1.0

140  |  Capítulo  5:  Primeros  pasos  con  los  pandas
Machine Translated by Google

En  [120]:  objeto[1]
Salida[120]:  1.0

En  [121]:  obj[2:4]
Salida[121]:  
C 2.0
3.0
d  tipo:  float64

En  [122]:  obj[['b',  'a',  'd']]
Salida[122]:  
b  1.0
a 0.0
d  3.0  dtipo:  
float64

En  [123]:  objeto[[1,  3]]
Salida[123]:  
b  1.0  d  3.0  
dtipo:  
float64

En  [124]:  obj[obj  <  2]
Salida[124]:  
a 0.0
1.0
tipo  b:  float64

La  segmentación  con  etiquetas  se  comporta  de  manera  diferente  a  la  segmentación  normal  de  Python  en  que  el  
punto  final  es  inclusivo:

En  [125]:  obj['b':'c']
Salida[125]:  
b  1.0
C 2.0
tipo:  float64

La  configuración  con  estos  métodos  modifica  la  sección  correspondiente  de  la  Serie:

En  [126]:  obj['b':'c']  =  5

Entrada  [127]:  obj  
Salida  [127]:  
a 0.0
b 5.0
C 5.0
d  3.0  dtipo:  
float64

La  indexación  en  un  DataFrame  es  para  recuperar  una  o  más  columnas  con  un  solo  valor  o  secuencia:

5.2  Funcionalidad  esencial  |  141
Machine Translated by Google

En  [128]:  data  =  pd.DataFrame(np.arange(16).reshape((4,  4)),  index=['Ohio',  
.....: 'Colorado',  'Utah',  'New  York'],  column=  ['uno',  'dos',  'tres',  'cuatro'])
.....:

En  [129]:  datos
Fuera[129]:
uno  dos  tres  CUATRO
Ohio 0 1 2 3
colorado  5 4  
Utah  9 8 6  10 7  11
Nueva  York  12  13 14 15

En  [130]:  datos['dos']
Salida[130]:
Ohio 1
Colorado 5
Utah 9
Nueva  York 13
Nombre:  dos,  dtype:  int64

En  [131]:  datos[['tres',  'uno']]
Fuera[131]:
tres  uno
Ohio 2  

Colorado 6 0  4
Utah 10 8
Nueva  York 14  12

La  indexación  como  esta  tiene  algunos  casos  especiales.  Primero,  cortando  o  seleccionando  datos  con  una  matriz  
booleana:

En  [132]:  datos[:2]
Fuera[132]:
uno  dos  tres  CUATRO
Ohio 0 1 2 3
Colorado 4 5 6 7

En  [133]:  datos[datos['tres']  >  5]
Fuera[133]:
uno  dos  tres  CUATRO
Colorado 4 5 6 7
Utah  9 8 10   11  
Nueva  York  12  13 14 15

La  sintaxis  de  selección  de  fila  data[:2]  se  proporciona  para  su  comodidad.  Pasar  un  solo  elemento  o  una  lista  al  
operador  []  selecciona  columnas.

Otro  caso  de  uso  es  la  indexación  con  un  DataFrame  booleano,  como  uno  producido  por  una  comparación  escalar:

142  |  Capítulo  5:  Primeros  pasos  con  los  pandas
Machine Translated by Google

En  [134]:  datos  <  5
Fuera[134]:
uno dos  tres  cuatro
Ohio Verdadero  Verdadero  Verdadero  Verdadero
Colorado  Verdadero  Falso  Falso  Falso
Utah Falso  Falso  Falso  Falso
Nueva  York  Falso  Falso  Falso  Falso

En  [135]:  datos[datos  <  5]  =  0

En  [136]:  datos
Fuera[136]:
uno  dos  tres  CUATRO
Ohio 0   0   0   0  
Colorado 0 5 6 7
Utah  9 8 10 11
Nueva  York  12  13 14 15

Esto  hace  que  DataFrame  sea  sintácticamente  más  como  una  matriz  NumPy  bidimensional  en  este  caso  
particular.

Selección  con  loc  e  iloc
Para  la  indexación  de  etiquetas  de  DataFrame  en  las  filas,  introduzco  los  operadores  especiales  de  
indexación  loc  e  iloc.  Le  permiten  seleccionar  un  subconjunto  de  filas  y  columnas  de  un  DataFrame  con  
notación  similar  a  NumPy  utilizando  etiquetas  de  eje  (loc)  o  números  enteros  (iloc).

Como  ejemplo  preliminar,  seleccionemos  una  sola  fila  y  varias  columnas  por  etiqueta:

En  [137]:  data.loc['Colorado',  ['dos',  'tres']]
Fuera[137]:
dos 5
tres 6
Nombre:  Colorado,  tipo  de  d:  int64

Luego  realizaremos  algunas  selecciones  similares  con  números  enteros  usando  iloc:

En  [138]:  datos.iloc[2,  [3,  0,  1]]
Fuera[138]:  
cuatro  11  8
uno
dos 9
Nombre:  Utah,  tipo  de  d:  int64

En  [139]:  datos.iloc[2]
Fuera[139]:
uno 8
dos   9
tres 10
cuatro 11
Nombre:  Utah,  tipo  de  d:  int64

5.2  Funcionalidad  esencial  |  143
Machine Translated by Google

En  [140]:  datos.iloc[[1,  2],  [3,  0,  1]]
Fuera[140]:
cuatro  uno  dos  7  5
Colorado 0
Utah 11 8 9

Ambas  funciones  de  indexación  funcionan  con  sectores  además  de  etiquetas  individuales  o  listas  de  etiquetas:

En  [141]:  data.loc[:'Utah',  'dos']
Fuera[141]:  
Ohio   0
Colorado 5
Utah 9
Nombre:  dos,  dtype:  int64

En  [142]:  datos.iloc[:,  :3][datos.tres  >  5]
Fuera[142]:
uno  dos  tres  5  6
Colorado 0
Utah  9 8 10
Nueva  York  12  13 14

Entonces,  hay  muchas  formas  de  seleccionar  y  reorganizar  los  datos  contenidos  en  un  objeto  pandas.
Para  DataFrame,  la  Tabla  5­4  proporciona  un  breve  resumen  de  muchos  de  ellos.  Como  verá  más  adelante,  hay  
varias  opciones  adicionales  para  trabajar  con  índices  jerárquicos.

Cuando  diseñé  pandas  originalmente,  sentí  que  tener  que  escribir  frame[:,  col]  
para  seleccionar  una  columna  era  demasiado  detallado  (y  propenso  a  errores),  
ya  que  la  selección  de  columnas  es  una  de  las  operaciones  más  comunes.  Hice  
el  compromiso  de  diseño  para  impulsar  todo  el  comportamiento  de  indexación  
elegante  (tanto  etiquetas  como  números  enteros)  en  el  operador  ix .  En  la  
práctica,  esto  condujo  a  muchos  casos  extremos  en  datos  con  etiquetas  de  eje  
de  enteros,  por  lo  que  el  equipo  de  pandas  decidió  crear  los  operadores  loc  e  iloc  
para  manejar  la  indexación  estrictamente  basada  en  etiquetas  y  en  enteros,  respectivamente.

El  operador  de  indexación  ix  todavía  existe,  pero  está  obsoleto.  No  recomiendo  
usarlo.

Tabla  5­4.  Opciones  de  indexación  con  DataFrame
notas

Escriba  df[val] Seleccione  una  sola  columna  o  secuencia  de  columnas  del  DataFrame;  conveniencias  de  

casos  especiales:  matriz  booleana  (filas  de  filtro),  división  (filas  de  división)  o  trama  de  datos  
booleana  (valores  establecidos  según  algún  criterio)

df.loc[valor] Selecciona  una  sola  fila  o  un  subconjunto  de  filas  del  DataFrame  por  etiqueta

df.loc[:,  val] Selecciona  una  sola  columna  o  subconjunto  de  columnas  por  etiqueta

df.loc[val1,  val2] Seleccionar  filas  y  columnas  por  etiqueta

df.iloc[donde] Selecciona  una  sola  fila  o  un  subconjunto  de  filas  del  DataFrame  por  posición  entera

144  |  Capítulo  5:  Primeros  pasos  con  los  pandas
Machine Translated by Google

notas

Escriba  df.iloc[:,  where]   Selecciona  una  sola  columna  o  subconjunto  de  columnas  por  posición  entera

df.iloc[where_i,  where_j]  Seleccione  filas  y  columnas  por  posición  de  entero  Seleccione  un  solo  valor  escalar  por  

df.at[etiqueta_i,  etiqueta_j] etiqueta  de  fila  y  columna  Seleccione  un  solo  valor  escalar  por  

df.iat[i,  j] posición  de  fila  y  columna  (enteros)

método  de  reindexación Seleccionar  filas  o  columnas  por  etiquetas  

Métodos  get_value,  set_value  Seleccionar  valor  único  por  etiqueta  de  fila  y  columna

Índices  enteros  

Trabajar  con  objetos  pandas  indexados  por  enteros  es  algo  que  a  menudo  hace  tropezar  a  los  
nuevos  usuarios  debido  a  algunas  diferencias  con  la  semántica  de  indexación  en  las  estructuras  
de  datos  integradas  de  Python,  como  listas  y  tuplas.  Por  ejemplo,  es  posible  que  no  espere  que  
el  siguiente  código  genere  un  error:

ser  =  pd.Series(np.arange(3.))
ser
ser[­1]

En  este  caso,  los  pandas  podrían  "recurrir"  a  la  indexación  de  enteros,  pero  es  difícil  hacer  esto  en  general  
sin  introducir  errores  sutiles.  Aquí  tenemos  un  índice  que  contiene  0,  1,  2,  pero  inferir  lo  que  quiere  el  
usuario  (indexación  basada  en  etiquetas  o  basada  en  la  posición)  es  difícil:

Entrada  [144]:  ser  
Salida[144]:  
0  0.0
1 1.0
2 2.0
tipo:  float64

Por  otro  lado,  con  un  índice  no  entero,  no  hay  posibilidad  de  ambigüedad:

En  [145]:  ser2  =  pd.Series(np.arange(3.),  index=['a',  'b',  'c'])

En  [146]:  ser2[­1]
Salida[146]:  2.0

Para  mantener  la  coherencia,  si  tiene  un  índice  de  eje  que  contiene  números  enteros,  la  selección  de  
datos  siempre  estará  orientada  a  etiquetas.  Para  un  manejo  más  preciso,  use  loc  (para  etiquetas)  o  iloc  
(para  números  enteros):

En  [147]:  ser[:1]
Salida[147]:  
0  0.0
tipo:  float64

En  [148]:  ser.loc[:1]
Salida[148]:  
0  0.0
1 1.0

5.2  Funcionalidad  esencial  |  145
Machine Translated by Google

tipo:  float64

En  [149]:  ser.iloc[:1]
Salida[149]:  
0  0.0  dtipo:  
float64

Aritmética  y  alineación  de  datos  Una  

característica  importante  de  pandas  para  algunas  aplicaciones  es  el  comportamiento  de  la  
aritmética  entre  objetos  con  diferentes  índices.  Cuando  está  sumando  objetos,  si  algún  par  
de  índices  no  es  el  mismo,  el  índice  respectivo  en  el  resultado  será  la  unión  de  los  pares  de  
índices.  Para  los  usuarios  con  experiencia  en  bases  de  datos,  esto  es  similar  a  una  unión  
externa  automática  en  las  etiquetas  de  índice.  Veamos  un  ejemplo:

En  [150]:  s1  =  pd.Series([7.3,  ­2.5,  3.4,  1.5],  index=['a',  'c',  'd',  'e'])

En  [151]:  s2  =  pd.Series([­2.1,  3.6,  ­1.5,  4,  3.1],  index=['a',  'c',  'e',  'f',  'g'])
.....:

En  [152]:  s1
Fuera[152]:
a 7.3
c  ­2,5  d  3,4

mi 1.5
tipo:  float64

Entrada  [153]:  
s2  Salida  
[153]:  a  ­2.1
C 3.6
e  ­1.5
F 4,0  
3,1  
g  tipo:  float64

Sumando  estos  rendimientos  juntos:

Entrada  [154]:  s1  +  s2  
Salida  [154]:  
a 5.2
C 1.1
d
mi NaN  0.0
F Yaya
Yaya

tipo  g:  float64

La  alineación  de  datos  internos  introduce  valores  faltantes  en  las  ubicaciones  de  las  etiquetas  que  no  se  
superponen.  Los  valores  faltantes  se  propagarán  luego  en  más  cálculos  aritméticos.

146  |  Capítulo  5:  Primeros  pasos  con  los  pandas
Machine Translated by Google

En  el  caso  de  DataFrame,  la  alineación  se  realiza  tanto  en  las  filas  como  en  las  columnas:

En  [155]:  df1  =  pd.DataFrame(np.arange(9.).reshape((3,  3)),  columnas=lista('bcd'),
.....: índice=['Ohio',  'Texas',  'Colorado'])

En  [156]:  df2  =  pd.DataFrame(np.arange(12.).reshape((4,  3)),  column=list('bde'),  index=['Utah',  'Ohio',  'Texas  ',  'Oregón'])
.....:

En  [157]:  df1
Fuera[157]:
b C d
Ohio 0,0  1,0  2,0
Texas  3,0  4,0  5,0  Colorado  6,0  7,0  
8,0

En  [158]:  df2
Fuera[158]:
b d
Utah e  0.0  1.0  2.0
Ohio 3,0  4,0  5,0
Tejas  6,0  7,0  8,0
Oregón  9,0  10,0  11,0

Al  sumar  estos,  se  obtiene  un  DataFrame  cuyo  índice  y  columnas  son  las  uniones  de  los  de  
cada  DataFrame:

En  [159]:  df1  +  df2
Fuera[159]:
b C Delaware

Colorado  NaN  NaN  NaN  NaN
Ohio 3.0  NaN  6.0  NaN  NaN  
Oregón NaN  NaN  NaN
Texas 9,0  NaN  12,0  NaN
Utah NaN  NaN  NaN  NaN

Dado  que  las  columnas  'c'  y  'e'  no  se  encuentran  en  ambos  objetos  DataFrame,  aparecen  como  
faltantes  en  el  resultado.  Lo  mismo  vale  para  las  filas  cuyas  etiquetas  no  son  comunes  a  ambos  
objetos.

Si  agrega  objetos  DataFrame  sin  etiquetas  de  columna  o  fila  en  común,  el  resultado  contendrá  
todos  los  valores  nulos:

En  [160]:  df1  =  pd.DataFrame({'A':  [1,  2]})

En  [161]:  df2  =  pd.DataFrame({'B':  [3,  4]})

En  [162]:  df1
Fuera[162]:
A
0  1
1  2

En  [163]:  df2

5.2  Funcionalidad  esencial  |  147
Machine Translated by Google

Fuera[163]:
B
0  3
1  4

En  [164]:  df1  ­  df2
Fuera[164]:
AB
0  NaN  NaN
1  NaN  NaN

Métodos  aritméticos  con  valores  de  relleno

En  las  operaciones  aritméticas  entre  objetos  indexados  de  manera  diferente,  es  posible  que  desee  
completar  con  un  valor  especial,  como  0,  cuando  se  encuentra  una  etiqueta  de  eje  en  un  objeto  pero  no  en  el  otro:

En  [165]:  df1  =  pd.DataFrame(np.arange(12.).reshape((3,  4)),  column=list('abcd'))
.....:

En  [166]:  df2  =  pd.DataFrame(np.arange(20.).reshape((4,  5)),  column=list('abcde'))
.....:

En  [167]:  df2.loc[1,  'b']  =  np.nan

En  [168]:  df1
Fuera[168]:
a b C d
0  0,0  1,0  2,0  3,0
1  4,0  5,0  6,0  7,0
2  8,0  9,0  10,0  11,0

En  [169]:  df2
Fuera[169]:
a b C d mi

0  0,0  1,0  2,0  3,0  4,0
1  5,0  NaN  7,0  8,0  9,0  2  10,0  11,0  12,0  13,0  
14,0
3  15,0  16,0  17,0  18,0  19,0

La  suma  de  estos  da  como  resultado  valores  de  NA  en  las  ubicaciones  que  no  se  superponen:

En  [170]:  df1  +  df2
Fuera[170]:
a b C d mi

0  0,0  2,0  4,0  6,0  NaN
1  9,0  NaN  13,0  15,0  NaN  2  18,0  20,0  22,0  
24,0  NaN
3  NaN  NaN  NaN  NaN  NaN

Usando  el  método  add  en  df1,  paso  df2  y  un  argumento  a  fill_value:

En  [171]:  df1.add(df2,  fill_value=0)
Fuera[171]:

148  |  Capítulo  5:  Primeros  pasos  con  los  pandas
Machine Translated by Google

a b C d mi

0  0,0  2,0  4,0  6,0  4,0
1  9,0  5,0  13,0  15,0  9,0
2  18,0  20,0  22,0  24,0  14,0  3  15,0  16,0  17,0  
18,0  19,0

Consulte  la  Tabla  5­5  para  obtener  una  lista  de  los  métodos  Series  y  DataFrame  para  la  aritmética.  Cada  uno  de  ellos  
tiene  una  contraparte,  comenzando  con  la  letra  r,  que  tiene  argumentos  invertidos.  Así  que  estas  dos  declaraciones  son  
equivalentes:

En  [172]:  1 /  df1
Fuera[172]:
a b C
0 d  inf  1.000000  0.500000  0.333333
1  0,250000  0,200000  0,166667  0,142857
2  0,125000  0,111111  0,100000  0,090909

En  [173]:  df1.rdiv(1)
Fuera[173]:
a b C
0 d  inf  1.000000  0.500000  0.333333
1  0,250000  0,200000  0,166667  0,142857  2  0,125000  0,111111  
0,100000  0,090909

De  manera  relacionada,  al  volver  a  indexar  una  serie  o  un  marco  de  datos,  también  puede  especificar  un  valor  de  relleno  
diferente:

En  [174]:  df1.reindex(columnas=df2.columnas,  fill_value=0)
Fuera[174]:
a b C de  0  
0.0  1.0  2.0  3.0  0
1  4,0  5,0  6,0  7,0  0
2  8,0  9,0  10,0  11,0  0

Tabla  5­5.  Métodos  aritméticos  flexibles
Método Descripción

agregar,  agregar Métodos  para  la  suma  (+)

sub,  rsub Métodos  para  restar  (­)

div,  rdiv   Métodos  de  división  (/)  

floordiv,  rfloordiv  Métodos  de  división  de  piso  (//)

mul,  rmul Métodos  de  multiplicación  (*)

pow,  pow Métodos  de  exponenciación  (**)

Operaciones  entre  DataFrame  y  Series  Al  igual  que  

con  los  arreglos  NumPy  de  diferentes  dimensiones,  también  se  define  la  aritmética  entre  DataFrame  y  Series.  Primero,  
como  ejemplo  motivador,  considere  la  diferencia  entre  una  matriz  bidimensional  y  una  de  sus  filas:

5.2  Funcionalidad  esencial  |  149
Machine Translated by Google

En  [175]:  arr  =  np.arange(12.).reshape((3,  4))

Entrada  [176]:  
matriz  
Salida[176]:   1.,  2.,  3.],  5.,  6.,  7.],  9.,  
10.,  11.]])
matriz([[ 0.,  [ 4.,  [ 8.,

En  [177]:  arr[0]
Salida[177]:  matriz([ 0.,  1.,  2.,  3.])

En  [178]:  arr  ­  arr[0]
Salida[178]:  
matriz([[ 0.,  0.,  0.,  0.],  [ 4.,  4.,  4.,  4.],  [ 8.,  
8.,  8.,  8.]] )

Cuando  restamos  arr[0]  de  arr,  la  resta  se  realiza  una  vez  por  cada  fila.
Esto  se  conoce  como  transmisión  y  se  explica  con  más  detalle  en  relación  con  las  matrices  
NumPy  generales  en  el  Apéndice  A.  Las  operaciones  entre  un  DataFrame  y  una  Serie  son  
similares:

En  [179]:  marco  =  pd.DataFrame(np.arange(12.).reshape((4,  3)),  column=list('bde'),  
.....: index=['Utah',  'Ohio',  'Texas  ',  
.....: 'Oregón'])

En  [180]:  serie  =  cuadro.iloc[0]

En  [181]:  cuadro
Fuera[181]:
b d mi

Utah 0,0  1,0  2,0
Ohio 3,0  4,0  5,0
Tejas  6,0  7,0  8,0
Oregón  9,0  10,0  11,0

Entrada  [182]:  serie  
Salida  
[182]:  b  0.0
d 1.0  
mi 2.0  
Nombre:  Utah,  dtype:  float64

De  forma  predeterminada,  la  aritmética  entre  DataFrame  y  Series  coincide  con  el  índice  de  
Series  en  las  columnas  de  DataFrame,  transmitiendo  las  filas:

En  [183]:  cuadro  ­  serie
Fuera[183]:
b d mi

Utah 0.0  0.0  0.0
Ohio 3.0  3.0  3.0
Tejas  6,0  6,0  6,0
Oregón  9,0  9,0  9,0

150  |  Capítulo  5:  Primeros  pasos  con  los  pandas
Machine Translated by Google

Si  no  se  encuentra  un  valor  de  índice  en  las  columnas  de  DataFrame  o  en  el  índice  de  Series,  los  
objetos  se  reindexarán  para  formar  la  unión:

En  [184]:  serie2  =  pd.Series(rango(3),  índice=['b',  'e',  'f'])

En  [185]:  cuadro  +  serie2
Fuera[185]:
bd ef
Utah 0,0  NaN  3,0  NaN
Ohio 3,0  NaN  6,0  NaN
Texas  6,0  NaN  9,0  NaN  Oregón  9,0  
NaN  12,0  NaN

Si,  en  cambio,  desea  transmitir  sobre  las  columnas,  haciendo  coincidir  las  filas,  debe  usar  uno  de  
los  métodos  aritméticos.  Por  ejemplo:

En  [186]:  serie3  =  fotograma['d']

En  [187]:  cuadro
Fuera[187]:
b d mi

Utah 0,0  1,0  2,0
Ohio 3,0  4,0  5,0
Texas  6,0  7,0  8,0  Oregón  9,0  10,0  
11,0

En  [188]:  serie3
Fuera[188]:
Utah 1.0  
Ohio 4.0
Texas 7.0
Oregón 10.0
Nombre:  d,  dtipo:  float64

En  [189]:  frame.sub(serie3,  eje='índice')
Fuera[189]:
bd  Utah   mi

­1.0  0.0  1.0
Ohio  ­1,0  0,0  1,0  Texas  ­1,0  0,0  
1,0
Oregón  ­1,0  0,0  1,0

El  número  de  eje  que  pasa  es  el  eje  en  el  que  debe  coincidir.  En  este  caso,  nos  referimos  a  hacer  
coincidir  el  índice  de  fila  de  DataFrame  (axis  =  'index'  o  axis  =  0)  y  transmitirlo.

Aplicación  y  mapeo  de  funciones
NumPy  ufuncs  (métodos  de  matriz  de  elementos)  también  funcionan  con  objetos  pandas:

En  [190]:  marco  =  pd.DataFrame(np.random.randn(4,  3),  column=list('bde'),  index=['Utah',  'Ohio',  'Texas',  
.....: 'Oregon'] )

5.2  Funcionalidad  esencial  |  151
Machine Translated by Google

En  [191]:  cuadro
Fuera[191]:
b d mi

Utah  ­0,204708  0,478943  ­0,519439  Ohio  
­0,555730  1,965781  1,393406
Texas  0.092908  0.281746  0.769023
Oregón  1,246435  1,007189  ­1,296221

En  [192]:  np.abs(fotograma)
Fuera[192]:
b d mi

Utah 0.204708  0.478943  0.519439
Ohio 0.555730  1.965781  1.393406
Texas  0.092908  0.281746  0.769023  Oregón  
1.246435  1.007189  1.296221

Otra  operación  frecuente  es  aplicar  una  función  en  arreglos  unidimensionales  a  cada  columna  o  fila.  El  
método  de  aplicación  de  DataFrame  hace  exactamente  esto:

En  [193]:  f  =  lambda  x:  x.max()  ­  x.min()

En  [194]:  cuadro.aplicar(f)
Salida[194]:  
b  1.802165  d  
1.684034
mi 2.689627
tipo:  float64

Aquí,  la  función  f,  que  calcula  la  diferencia  entre  el  máximo  y  el  mínimo  de  una  serie,  se  invoca  una  vez  
en  cada  columna  del  cuadro.  El  resultado  es  una  Serie  que  tiene  como  índice  las  columnas  del  marco .

Si  pasa  axis='columns'  para  aplicar,  la  función  se  invocará  una  vez  por  fila  en  su  lugar:

En  [195]:  frame.apply(f,  eje='columnas')
Fuera[195]:  
Utah   0.998382
Ohio 2.521511
Texas  0.676115  Oregón  
2.542656  dtype:  float64

Muchas  de  las  estadísticas  de  matriz  más  comunes  (como  la  suma  y  la  media)  son  métodos  de  
DataFrame,  por  lo  que  no  es  necesario  usar  apply .

La  función  pasada  para  aplicar  no  necesita  devolver  un  valor  escalar;  también  puede  devolver  una  
Serie  con  múltiples  valores:

En  [196]:  def  f(x):  return  
.....: pd.Series([x.min(),  x.max()],  index=['min',  'max'])

En  [197]:  cuadro.aplicar(f)

152  |  Capítulo  5:  Primeros  pasos  con  los  pandas
Machine Translated by Google

Fuera[197]:
b d mi

min  ­0.555730  0.281746  ­1.296221
máx.  1,246435  1,965781  1,393406

También  se  pueden  usar  funciones  de  Python  basadas  en  elementos.  Suponga  que  desea  calcular  una  
cadena  formateada  a  partir  de  cada  valor  de  punto  flotante  en  el  marco.  Puedes  hacer  esto  con  aplicar
mapa:

En  [198]:  formato  =  lambda  x:  '%.2f'  %  x

En  [199]:  frame.applymap(formato)
Fuera[199]:
ser  ­0.20  d0.48  ­0.52
Utah
Ohio ­0,56  1,97  1,39
Texas 0,09  0,28  0,77
Oregón  1,25  1,01  ­1,30

El  motivo  del  nombre  applymap  es  que  Series  tiene  un  método  de  mapa  para  aplicar  una  función  por  elementos:

En  [200]:  marco['e'].mapa(formato)
Fuera[200]:  
Utah   ­0.52
Ohio 1.39
Texas 0.77
Oregón ­1.30
Nombre:  e,  dtype:  objeto

Clasificación  y  clasificación  La  

clasificación  de  un  conjunto  de  datos  por  algún  criterio  es  otra  operación  incorporada  importante.  Para  ordenar  
lexicográficamente  por  índice  de  fila  o  columna,  use  el  método  sort_index ,  que  devuelve  un  nuevo  objeto  
ordenado:

En  [201]:  obj  =  pd.Series(rango(4),  índice=['d',  'a',  'b',  'c'])

En  [202]:  obj.sort_index()
Salida[202]:  
a 1
b 2
C 3
d 0
tipo:  int64

Con  un  DataFrame,  puede  ordenar  por  índice  en  cualquiera  de  los  ejes:

En  [203]:  cuadro  =  pd.DataFrame(np.arange(8).reshape((2,  4)),
.....: índice=['tres',  'uno'],  columnas=['d',  
.....: 'a',  'b',  'c'])

En  [204]:  frame.sort_index()

5.2  Funcionalidad  esencial  |  153
Machine Translated by Google

Fuera[204]:
dabc
uno 4  5  6  7
tres  0  1  2  3

En  [205]:  frame.sort_index(eje=1)
Fuera[205]:
a  B  C  D
tres  1  2  3  0
uno 5  6  7  4

Los  datos  se  ordenan  en  orden  ascendente  de  forma  predeterminada,  pero  se  pueden  ordenar  en  orden  descendente
ordenar,  también:

En  [206]:  frame.sort_index(eje=1,  ascendente=Falso)
Fuera[206]:
dcba
tres  0  3  2  1
uno 4  7  6  5

Para  ordenar  una  Serie  por  sus  valores,  use  su  método  sort_values :

En  [207]:  obj  =  pd.Series([4,  7,  ­3,  2])

En  [208]:  obj.sort_values()
Fuera[208]:
2  ­3
3 2
0   4
1  7

tipo:  int64

Los  valores  que  faltan  se  ordenan  al  final  de  la  serie  de  forma  predeterminada:

En  [209]:  obj  =  pd.Series([4,  np.nan,  7,  np.nan,  ­3,  2])

En  [210]:  obj.sort_values()
Salida[210]:
4  ­3.0
5  2.0
0 4.0
2 7.0
1 Yaya
3 Yaya

tipo:  float64

Al  ordenar  un  DataFrame,  puede  usar  los  datos  en  una  o  más  columnas  como  orden
llaves.  Para  hacerlo,  pase  uno  o  más  nombres  de  columna  a  la  opción  by  de  sort_values:

En  [211]:  cuadro  =  pd.DataFrame({'b':  [4,  7,  ­3,  2],  'a':  [0,  1,  0,  1]})

En  [212]:  marco
Fuera[212]:
abdominales

154  |  Capítulo  5:  Primeros  pasos  con  los  pandas
Machine Translated by Google

0  0  4
1  1  7
2  0  ­3
3  1  2

En  [213]:  frame.sort_values(by='b')
Fuera[213]:
abdominales

2  0  ­3
3  1  2
0  0  4
1  1  7

Para  ordenar  por  varias  columnas,  pase  una  lista  de  nombres:

En  [214]:  frame.sort_values(by=['a',  'b'])
Fuera[214]:
abdominales

2  0  ­3
0  0  4
3  1  2
1  1  7

Clasificación  asigna  rangos  desde  uno  hasta  el  número  de  puntos  de  datos  válidos  en  una  matriz.
Los  métodos  de  rango  para  Series  y  DataFrame  son  el  lugar  para  buscar;  por  rango  predeterminado
rompe  los  empates  asignando  a  cada  grupo  el  rango  medio:

En  [215]:  obj  =  pd.Series([7,  ­5,  7,  4,  2,  0,  4])

En  [216]:  obj.rango()
Fuera[216]:
0  6.5
1  1.0
2 6.5
3 4.5
4 3.0
5  2.0
6  4.5
tipo:  float64

Los  rangos  también  se  pueden  asignar  de  acuerdo  con  el  orden  en  que  se  observan  en  el
datos:

En  [217]:  obj.rank(método='primero')
Fuera[217]:
0  6.0
1 1.0
2 7.0
3 4.0
4 3.0
5 2.0
6 5.0
tipo:  float64

5.2  Funcionalidad  esencial  |  155
Machine Translated by Google

Aquí,  en  lugar  de  usar  el  rango  promedio  6.5  para  las  entradas  0  y  2,  en  su  lugar  tienen
se  ha  establecido  en  6  y  7  porque  la  etiqueta  0  precede  a  la  etiqueta  2  en  los  datos.

También  puede  clasificar  en  orden  descendente:

#  Asigne  valores  de  empate  al  rango  máximo  en  el  grupo
En  [218]:  obj.rank(ascending=False,  method='max')
Fuera[218]:
0  2.0
1   7.0
2 2.0
3 4.0
4 5.0
5 6.0
6  4.0
tipo:  float64

Consulte  la  Tabla  5­6  para  obtener  una  lista  de  los  métodos  de  desempate  disponibles.

DataFrame  puede  calcular  rangos  sobre  las  filas  o  las  columnas:

En  [219]:  cuadro  =  pd.DataFrame({'b':  [4.3,  7,  ­3,  2],  'a':  [0,  1,  0,  1],
.....: 'c':  [­2,  5,  8,  ­2.5]})

En  [220]:  marco
Fuera[220]:
a b C
0  0  4,3  ­2,0
1  1  7,0  5,0
2  0  ­3,0  8,0
3  1  2,0  ­2,5

En  [221]:  frame.rank(axis='columns')
Fuera[221]:
a b C
0  2,0  3,0  1,0
1  1,0  3,0  2,0
2  2,0  1,0  3,0
3  2,0  3,0  1,0

Tabla  5­6.  Métodos  de  desempate  con  rango
Método Descripción

'promedio'  Predeterminado:  asigna  el  rango  promedio  a  cada  entrada  en  el  grupo  igual
'min' Use  el  rango  mínimo  para  todo  el  grupo

'máximo' Usar  el  rango  máximo  para  todo  el  grupo

'primero' Asigne  rangos  en  el  orden  en  que  aparecen  los  valores  en  los  datos

'denso' Me  gusta  method='min',  pero  los  rangos  siempre  aumentan  en  1  entre  grupos  en  lugar  del  número  de  iguales
elementos  en  un  grupo

156  |  Capítulo  5:  Primeros  pasos  con  los  pandas
Machine Translated by Google

Índices  de  ejes  con  etiquetas  duplicadas
Hasta  ahora,  todos  los  ejemplos  que  hemos  visto  tenían  etiquetas  de  eje  únicas  (índice
valores).  Si  bien  muchas  funciones  de  pandas  (como  reindexar)  requieren  que  las  etiquetas  sean
único,  no  es  obligatorio.  Consideremos  una  serie  pequeña  con  índices  duplicados:

En  [222]:  obj  =  pd.Series(rango(5),  índice=['a',  'a',  'b',  'b',  'c'])

En  [223]:  objeto
Fuera[223]:
a 0
a 1
2
cama  y  

3
desayuno

C 4
tipo:  int64

La  propiedad  is_unique  del  índice  puede  decirle  si  sus  etiquetas  son  únicas  o  no:

En  [224]:  obj.index.is_unique
Salida[224]:  Falso

La  selección  de  datos  es  una  de  las  cosas  principales  que  se  comporta  de  manera  diferente  con  los  duplicados.
Indexar  una  etiqueta  con  múltiples  entradas  devuelve  una  Serie,  mientras  que  las  entradas  individuales  devuelven  una
valor  escalar:

En  [225]:  objeto['a']
Fuera[225]:
a 0
a 1
tipo:  int64

En  [226]:  objeto['c']
Salida[226]:  4

Esto  puede  hacer  que  su  código  sea  más  complicado,  ya  que  el  tipo  de  salida  de  la  indexación  puede
varían  en  función  de  si  una  etiqueta  se  repite  o  no.

La  misma  lógica  se  extiende  a  la  indexación  de  filas  en  un  DataFrame:

En  [227]:  df  =  pd.DataFrame(np.random.randn(4,  3),  index=['a',  'a',  'b',  'b'])

En  [228]:  d.f.
Fuera[228]:
0 1 2
0.274992  0.228913  1.352917
0,886429  ­2,001637  ­0,371843  _
b  1.669025  ­0.438570  ­0.539741
b  0.476985  3.248944  ­1.021228

En  [229]:  df.loc['b']
Fuera[229]:
0 1 2

5.2  Funcionalidad  esencial  |  157
Machine Translated by Google

b  1.669025  ­0.438570  ­0.539741  b  
0.476985  3.248944  ­1.021228

5.3  Resumen  y  cálculo  de  estadísticas  descriptivas
Los  objetos  pandas  están  equipados  con  un  conjunto  de  métodos  matemáticos  y  estadísticos  comunes.  
La  mayoría  de  estos  entran  en  la  categoría  de  reducciones  o  estadísticas  de  resumen,  métodos  que  
extraen  un  valor  único  (como  la  suma  o  la  media)  de  una  Serie  o  una  Serie  de  valores  de  las  filas  o  
columnas  de  un  DataFrame.  En  comparación  con  los  métodos  similares  que  se  encuentran  en  las  
matrices  NumPy,  tienen  un  manejo  integrado  para  los  datos  faltantes.  Considere  un  pequeño  DataFrame:

En  [230]:  df  =  pd.DataFrame([[1.4,  np.nan],  [7.1,  ­4.5],
.....: [np.nan,  np.nan],  [0.75,  ­1.3]],
.....: índice=['a',  'b',  'c',  'd'],  columnas=['uno',  
.....: 'dos'])

En  [231]:  d.f.
Fuera[231]:
uno  dos
a  1,40  NaN  b  
7,10  ­4,5
c  NaN  NaN
d  0,75  ­1,3

Llamar  al  método  sum  de  DataFrame  devuelve  una  serie  que  contiene  sumas  de  columnas:

En  [232]:  df.suma()
Fuera[232]:
uno 9.25
dos  ­5.80  
dtype:  float64

Pasando  axis='columns'  o  axis=1  sumas  a  través  de  las  columnas  en  su  lugar:

En  [233]:  df.sum(eje='columnas')
Salida[233]:  
1,40  
2,60
abdominales

C
NaN  d  ­0.55  
dtipo:  float64

Los  valores  NA  se  excluyen  a  menos  que  todo  el  segmento  (fila  o  columna  en  este  caso)  sea  NA.
Esto  se  puede  desactivar  con  la  opción  skipna :

En  [234]:  df.mean(axis='columns',  skipna=False)
Salida[234]:  
a NaN
b 1.300
C Yaya

158  |  Capítulo  5:  Primeros  pasos  con  los  pandas
Machine Translated by Google

d  ­0.275  dtipo:  
float64

Consulte  la  Tabla  5­7  para  obtener  una  lista  de  opciones  comunes  para  cada  método  de  reducción.

Tabla  5­7.  Opciones  para  métodos  de  reducción

Método  Descripción  
eje  Eje  a  reducir;  0  para  filas  de  DataFrame  y  1  para  columnas
skipna  Excluye  valores  faltantes;  Verdadero  por  nivel  

predeterminado  Reducir  agrupados  por  nivel  si  el  eje  está  indexado  jerárquicamente  (MultiIndex)

Algunos  métodos,  como  idxmin  e  idxmax,  devuelven  estadísticas  indirectas  como  el  valor  del  índice  donde  se  alcanzan  
los  valores  mínimo  o  máximo:

En  [235]:  df.idxmax()
Salida[235]:  
uno b
dos   d
dtype:  objeto

Otros  métodos  son  las  acumulaciones:

En  [236]:  df.cumsum()
Fuera[236]:
uno  dos
a  1,40  NaN  b  
8,50  ­4,5
c  NaN  NaN  d  
9,25  ­5,8

Otro  tipo  de  método  no  es  ni  una  reducción  ni  una  acumulación.  describe  es  uno  de  esos  ejemplos,  que  produce  múltiples  
estadísticas  de  resumen  de  una  sola  vez:

En  [237]:  df.describe()
Fuera[237]:
uno dos
cuenta  3.000000  2.000000
media  3,083333  ­2,900000  estándar  
3,493685  2,262742  min
0.750000  ­4.500000
25% 1.075000  ­3.700000
50% 1.400000  ­2.900000
75% 4.250000  ­2.100000  
máximo 7.100000  ­1.300000

En  datos  no  numéricos,  describe  produce  estadísticas  de  resumen  alternativas:

En  [238]:  obj  =  pd.Series(['a',  'a',  'b',  'c']  *  4)

En  [239]:  obj.describe()
Fuera[239]:  
cuenta dieciséis

5.3  Resumen  y  cálculo  de  estadísticas  descriptivas  |  159
Machine Translated by Google

único  tipo   3

de   a
8

frecuencia  superior:  objeto

Consulte  la  Tabla  5­8  para  obtener  una  lista  completa  de  estadísticas  de  resumen  y  métodos  relacionados.

Tabla  5­8.  Estadísticas  descriptivas  y  resumidas
Método Descripción

contar Número  de  valores  no  NA

describir Calcule  un  conjunto  de  estadísticas  de  resumen  para  Series  o  cada  columna  de  DataFrame

min,  max   Calcular  valores  mínimos  y  máximos

argmin,  argmax  Calcular  ubicaciones  de  índice  (enteros)  en  las  que  se  obtuvo  el  valor  mínimo  o  máximo,  respectivamente

idxmin,  idxmax  Calcule  las  etiquetas  de  índice  en  las  que  se  obtuvo  el  valor  mínimo  o  máximo,  respectivamente

cuantil Calcule  el  cuantil  de  muestra  que  va  de  0  a  1

suma Suma  de  valores

significar Media  de  valores

mediana Mediana  aritmética  (50%  cuantil)  de  valores

enojado Desviación  absoluta  media  del  valor  medio

Producto  de  todos  los  valores
pinchar

variable Ejemplo  de  varianza  de  valores

estándar Ejemplo  de  desviación  estándar  de  valores

sesgar Sesgo  muestral  (tercer  momento)  de  los  valores

kurt Ejemplo  de  curtosis  (cuarto  momento)  de  valores

cumsum Suma  acumulada  de  valores

cummin,  cummax  Mínimo  o  máximo  acumulativo  de  valores,  respectivamente

Cumprod Producto  acumulativo  de  valores

diferencia Calcular  la  primera  diferencia  aritmética  (útil  para  series  de  tiempo)

cambio_pct Calcular  cambios  porcentuales

Correlación  y  Covarianza
Algunas  estadísticas  de  resumen,  como  la  correlación  y  la  covarianza,  se  calculan  a  partir  de  pares  de
argumentos  Consideremos  algunos  DataFrames  de  precios  de  acciones  y  volúmenes  obtenidos
de  Yahoo!  Finanzas  utilizando  el  paquete  adicional  pandas­datareader .  si  no  lo  haces
tenerlo  ya  instalado,  se  puede  obtener  a  través  de  conda  o  pip:

conda  instalar  pandas­datareader

Utilizo  el  módulo  pandas_datareader  para  descargar  algunos  datos  de  algunas  cotizaciones  bursátiles:

importar  pandas_datareader.data  como  web
all_data  =  {marca:  web.get_data_yahoo(marca)
para  ticker  en  ['AAPL',  'IBM',  'MSFT',  'GOOG']}

160  |  Capítulo  5:  Primeros  pasos  con  los  pandas
Machine Translated by Google

precio  =  pd.DataFrame({ticker:  data['Adj  Close']
para  teletipo,  datos  en  all_data.items()})  volumen  =  
pd.DataFrame({ticker:  data['Volume']  para  teletipo,  datos  en  
all_data.items()})

Es  posible  en  el  momento  en  que  estés  leyendo  esto  que  Yahoo!  Finance  
ya  no  existe  desde  que  Yahoo!  fue  adquirida  por  Verizon  en  2017.  
Consulte  la  documentación  en  línea  de  pandas­datareader  para  obtener  
la  última  funcionalidad.

Ahora  calculo  los  cambios  porcentuales  de  los  precios,  una  operación  de  series  de  tiempo  que  se  explorará  
más  adelante  en  el  Capítulo  11:

En  [242]:  devuelve  =  precio.pct_change()

En  [243]:  devuelve.tail()
Fuera[243]:
AAPL GOOG IBM MSFT
Fecha
2016­10­17  ­0.000680  0.001837  0.002072  ­0.003483  2016­10­18  ­0.000681  
0.019616  ­0.026168  0.007690
2016­10­19  ­0.002979  0.007846  0.003583  ­0.002255
2016­10­20  ­0.000512  ­0.005652  0.001719  ­0.004867
2016­10­21  ­0.003930  0.003011  ­0.012474  0.042096

El  método  corr  de  Series  calcula  la  correlación  de  los  valores  superpuestos,  no  NA,  alineados  por  índice  en  
dos  Series.  De  manera  relacionada,  cov  calcula  la  covarianza:

En  [244]:  devuelve['MSFT'].corr(devuelve['IBM'])
Salida[244]:  0.49976361144151144

En  [245]:  devuelve['MSFT'].cov(devuelve['IBM'])
Salida[245]:  8.8706554797035462e­05

Dado  que  MSFT  es  un  atributo  válido  de  Python,  también  podemos  seleccionar  estas  columnas  usando  una  
sintaxis  más  concisa:

En  [246]:  devoluciones.MSFT.corr(devoluciones.IBM)
Salida[246]:  0.49976361144151144

Los  métodos  corr  y  cov  de  DataFrame ,  por  otro  lado,  devuelven  una  matriz  de  correlación  o  covarianza  
completa  como  un  DataFrame,  respectivamente:

En  [247]:  devuelve.corr()
Fuera[247]:
AAPL GOOG IBM MSFT
AAPL  1,000000  0,407919  0,386817  0,389695  GOOG  0,407919  
1,000000  0,405099  0,465919  IBM  0,386817  0,405099  1,000000  
0,499764
MSFT  0,389695  0,465919  0,499764  1,000000

5.3  Resumen  y  cálculo  de  estadísticas  descriptivas  |  161
Machine Translated by Google

En  [248]:  devuelve.cov()
Fuera[248]:
AAPL GOOG IBM MSFT
AAPL  0,000277  0,000107  0,000078  0,000095  GOOG  0,000107  
0,000251  0,000078  0,000108
IBM  0,000078  0,000078  0,000146  0,000089
MSFT  0,000095  0,000108  0,000089  0,000215

Con  el  método  corrwith  de  DataFrame ,  puede  calcular  las  correlaciones  por  pares  entre  las  columnas  
o  filas  de  un  DataFrame  con  otra  Serie  o  DataFrame.  Pasar  una  Serie  devuelve  una  Serie  con  el  valor  
de  correlación  calculado  para  cada  columna:

En  [249]:  devuelve.corrwith(devuelve.IBM)
Salida[249]:  
AAPL 0.386817
GOOG 0.405099
IBM  1.000000  MSFT  
0.499764  dtype:  float64

Pasar  un  DataFrame  calcula  las  correlaciones  de  los  nombres  de  columna  coincidentes.  Aquí  calculo  
las  correlaciones  de  los  cambios  porcentuales  con  el  volumen:

En  [250]:  devuelve.corrwith(volumen)
Salida[250]:  
AAPL  ­0.075565
GOOG  ­0.007067
IBM ­0.204849
MSFT  ­0.092950
tipo:  float64

Pasar  axis='columns'  hace  las  cosas  fila  por  fila  en  su  lugar.  En  todos  los  casos,  los  puntos  de  datos  
se  alinean  por  etiqueta  antes  de  calcular  la  correlación.

Valores  únicos,  recuentos  de  valor  y  membresía
Otra  clase  de  métodos  relacionados  extrae  información  sobre  los  valores  contenidos  en  una  Serie  
unidimensional.  Para  ilustrar  esto,  considere  este  ejemplo:

En  [251]:  obj  =  pd.Series(['c',  'a',  'd',  'a',  'a',  'b',  'b',  'c',  'c'])

La  primera  función  es  única,  lo  que  le  brinda  una  matriz  de  valores  únicos  en  una  Serie:

En  [252]:  únicos  =  obj.unique()

En  [253]:  únicos
Salida[253]:  matriz(['c',  'a',  'd',  'b'],  dtype=objeto)

Los  valores  únicos  no  se  devuelven  necesariamente  en  orden  ordenado,  pero  se  pueden  ordenar  
después  del  hecho  si  es  necesario  (uniques.sort()).  De  manera  relacionada,  value_counts  calcula  una  
serie  que  contiene  frecuencias  de  valor:

162  |  Capítulo  5:  Primeros  pasos  con  los  pandas
Machine Translated by Google

En  [254]:  obj.value_counts()
Fuera[254]:
C 3
3
2
1
abd  tipo:  int64

La  serie  se  ordena  por  valor  en  orden  descendente  para  mayor  comodidad.  value_counts  es
también  disponible  como  un  método  de  pandas  de  nivel  superior  que  se  puede  usar  con  cualquier  matriz  o
secuencia:

En  [255]:  pd.value_counts(obj.values,  sort=False)
Fuera[255]:
a 3
b 2
3
disco  1

tipo:  int64

isin  realiza  una  verificación  de  membresía  de  conjunto  vectorizado  y  puede  ser  útil  para  filtrar  un
conjunto  de  datos  hasta  un  subconjunto  de  valores  en  una  serie  o  columna  en  un  marco  de  datos:

En  [256]:  objeto
Fuera[256]:
0 C
1 a
2 d
3 a
4 a
5 b
6   b
7 C
8 C

tipo:  objeto

En  [257]:  máscara  =  obj.isin(['b',  'c'])

En  [258]:  máscara
Fuera[258]:
0 Verdadero

1 FALSO
2 FALSO
3 FALSO
4 FALSO
5 Verdadero

6   Verdadero

7 Verdadero

8 Verdadero

dtipo:  booleano

En  [259]:  obj[máscara]
Fuera[259]:

5.3  Resumen  y  cálculo  de  estadísticas  descriptivas  |  163
Machine Translated by Google

0 C
5 b
6 b
C
C

7  8  dtipo:  objeto

Relacionado  con  isin  está  el  método  Index.get_indexer ,  que  le  brinda  una  matriz  de  índice
de  una  matriz  de  valores  posiblemente  no  distintos  a  otra  matriz  de  valores  distintos:

En  [260]:  to_match  =  pd.Series(['c',  'a',  'b',  'b',  'c',  'a'])

En  [261]:  valores_únicos  =  pd.Series(['c',  'b',  'a'])

En  [262]:  pd.Index(unique_vals).get_indexer(to_match)
Salida[262]:  matriz([0,  2,  1,  1,  0,  2])
Consulte  la  Tabla  5­9  para  obtener  una  referencia  sobre  estos  métodos.

Tabla  5­9.  Únicos,  recuentos  de  valores  y  métodos  de  membresía  establecidos

Método Descripción

es  en Calcule  la  matriz  booleana  que  indica  si  cada  valor  de  Serie  está  contenido  en  la  secuencia  pasada  de
valores

fósforo Calcule  índices  enteros  para  cada  valor  en  una  matriz  en  otra  matriz  de  valores  distintos;  útil  para  los  datos
operaciones  de  alineación  y  tipo  unión

unique   Calcule  una  matriz  de  valores  únicos  en  una  serie,  devueltos  en  el  orden  observado

value_counts  Devuelve  una  serie  que  contiene  valores  únicos  como  su  índice  y  frecuencias  como  sus  valores,  conteo  ordenado  en
orden  descendiente

En  algunos  casos,  es  posible  que  desee  calcular  un  histograma  en  varias  columnas  relacionadas  en
una  trama  de  datos.  Aquí  hay  un  ejemplo:

En  [263]:  datos  =  pd.DataFrame({'Qu1':  [1,  3,  4,  3,  4],
.....: 'Qu2':  [2,  3,  1,  2,  3],
.....: 'Qu3':  [1,  5,  2,  4,  4]})

En  [264]:  datos
Fuera[264]:
Qu1  Qu2  Qu3
0 1 2 1
1 3   3   5
2   4   1   2
3 3 2 4
4 4 3 4

Pasar  pandas.value_counts  a  la  función  de  aplicación  de  este  DataFrame  da:

En  [265]:  resultado  =  data.apply(pd.value_counts).fillna(0)

En  [266]:  resultado
Fuera[266]:

164  |  Capítulo  5:  Primeros  pasos  con  los  pandas
Machine Translated by Google

Qu1  Qu2  Qu3  1  1,0  
1,0  1,0
2  0,0  2,0  1,0
3  2,0  2,0  0,0  4  2,0  0,0  
2,0
5  0,0  0,0  1,0

Aquí,  las  etiquetas  de  las  filas  en  el  resultado  son  los  distintos  valores  que  ocurren  en  todas  las  
columnas.  Los  valores  son  los  recuentos  respectivos  de  estos  valores  en  cada  columna.

5.4  Conclusión
En  el  próximo  capítulo,  discutiremos  las  herramientas  para  leer  (o  cargar)  y  escribir  conjuntos  de  
datos  con  pandas.  Después  de  eso,  profundizaremos  en  las  herramientas  de  limpieza,  disputa,  
análisis  y  visualización  de  datos  usando  pandas.

5.4  Conclusión  |  165
Machine Translated by Google
Machine Translated by Google

CAPÍTULO  6

Carga  de  datos,  almacenamiento  y  formatos  de  archivo

Acceder  a  los  datos  es  un  primer  paso  necesario  para  utilizar  la  mayoría  de  las  herramientas  de  este  libro.  Soy
se  centrará  en  la  entrada  y  salida  de  datos  usando  pandas,  aunque  hay  numerosos
Otras  herramientas  en  otras  bibliotecas  para  ayudar  con  la  lectura  y  escritura  de  datos  en  varios  formatos.

La  entrada  y  la  salida  generalmente  se  dividen  en  algunas  categorías  principales:  lectura  de  archivos  de  texto  y  otros
formatos  en  disco  más  eficientes,  cargando  datos  desde  bases  de  datos  e  interactuando  con  redes
fuentes  de  trabajo  como  las  API  web.

6.1  Leer  y  escribir  datos  en  formato  de  texto
pandas  presenta  una  serie  de  funciones  para  leer  datos  tabulares  como  un  DataFrame
objeto.  La  Tabla  6­1  resume  algunos  de  ellos,  aunque  read_csv  y  read_table  son
probablemente  los  que  más  usará.

Tabla  6­1.  Funciones  de  análisis  en  pandas

Función Descripción

read_csv   Cargue  datos  delimitados  desde  un  archivo,  URL  u  objeto  similar  a  un  archivo;  usar  coma  como  delimitador  predeterminado

read_table  read_fwf   Cargue  datos  delimitados  desde  un  archivo,  URL  u  objeto  similar  a  un  archivo;  use  la  pestaña  ('\  t')  como  delimitador  predeterminado

read_clipboard   Leer  datos  en  formato  de  columna  de  ancho  fijo  (es  decir,  sin  delimitadores)

Versión  de  read_table  que  lee  datos  del  portapapeles;  útil  para  convertir  tablas  de  la  web

paginas

read_excel  read_hdf   Leer  datos  tabulares  de  un  archivo  Excel  XLS  o  XLSX

read_html   Leer  archivos  HDF5  escritos  por  pandas

read_json   Leer  todas  las  tablas  que  se  encuentran  en  el  documento  HTML  dado

read_msgpack   Leer  datos  de  una  representación  de  cadena  JSON  (Notación  de  objetos  de  JavaScript)

Leer  datos  de  pandas  codificados  usando  el  formato  binario  MessagePack

leer_pickle Lea  un  objeto  arbitrario  almacenado  en  formato  pickle  de  Python

167
Machine Translated by Google

Función Descripción

leer_sas Lea  un  conjunto  de  datos  SAS  almacenado  en  uno  de  los  formatos  de  almacenamiento  personalizados  del  sistema  SAS

leer_sql Lea  los  resultados  de  una  consulta  SQL  (usando  SQLAlchemy)  como  un  marco  de  datos  de  pandas

read_stata   Leer  un  conjunto  de  datos  del  formato  de  archivo  Stata

read_feather  Leer  el  formato  de  archivo  binario  Feather

Daré  una  descripción  general  de  la  mecánica  de  estas  funciones,  que  están  destinadas  a  convertir  datos  de  texto  
en  un  DataFrame.  Los  argumentos  opcionales  para  estas  funciones  pueden  caer  en  algunas  categorías:

Indexación  
Puede  tratar  una  o  más  columnas  como  el  DataFrame  devuelto  y  si  obtener  los  nombres  de  las  columnas  
del  archivo,  el  usuario  o  no  obtenerlos.

Inferencia  de  tipos  y  conversión  de  datos  
Esto  incluye  las  conversiones  de  valores  definidas  por  el  usuario  y  la  lista  personalizada  de  marcadores  de  
valores  perdidos.

Análisis  de  fecha  y  
hora  Incluye  capacidad  de  combinación,  incluida  la  combinación  de  información  de  fecha  y  hora  distribuida  
en  varias  columnas  en  una  sola  columna  en  el  resultado.

Iteración  
Soporte  para  iterar  sobre  fragmentos  de  archivos  muy  grandes.

Problemas  de  datos  

sucios  Omitir  filas  o  un  pie  de  página,  comentarios  u  otras  cosas  menores  como  datos  numéricos  con  miles  
separados  por  comas.

Debido  a  lo  desordenados  que  pueden  ser  los  datos  en  el  mundo  real,  algunas  de  las  funciones  de  carga  de  datos  
(especialmente  read_csv)  se  han  vuelto  muy  complejas  en  sus  opciones  con  el  tiempo.  Es  normal  sentirse  
abrumado  por  la  cantidad  de  parámetros  diferentes  (read_csv  tiene  más  de  50  al  momento  de  escribir  este  
artículo).  La  documentación  de  pandas  en  línea  tiene  muchos  ejemplos  sobre  cómo  funciona  cada  uno  de  ellos,  
por  lo  que  si  tiene  dificultades  para  leer  un  archivo  en  particular,  puede  haber  un  ejemplo  lo  suficientemente  similar  
para  ayudarlo  a  encontrar  los  parámetros  correctos.

Algunas  de  estas  funciones,  como  pandas.read_csv,  realizan  inferencias  de  tipos,  porque  los  tipos  de  datos  de  las  
columnas  no  forman  parte  del  formato  de  datos.  Eso  significa  que  no  necesariamente  tiene  que  especificar  qué  
columnas  son  numéricas,  enteras,  booleanas  o  de  cadena.  Otros  formatos  de  datos,  como  HDF5,  Feather  y  
msgpack,  tienen  los  tipos  de  datos  almacenados  en  el  formato.

El  manejo  de  fechas  y  otros  tipos  personalizados  puede  requerir  un  esfuerzo  adicional.  Comencemos  con  un  
pequeño  archivo  de  texto  separado  por  comas  (CSV):

En  [8]: !cat  ejemplos/ex1.csv  
a,b,c,d,mensaje  
1,2,3,4,hola

168  |  Capítulo  6:  Carga  de  datos,  almacenamiento  y  formatos  de  archivo
Machine Translated by Google

5,6,7,8,mundo
9,10,11,12,foo

Aquí  usé  el  comando  Unix  cat  shell  para  imprimir  el  contenido  sin  procesar
del  archivo  a  la  pantalla.  Si  está  en  Windows,  puede  usar  el  tipo
en  lugar  de  gato  para  lograr  el  mismo  efecto.

Dado  que  está  delimitado  por  comas,  podemos  usar  read_csv  para  leerlo  en  un  DataFrame:

En  [9]:  df  =  pd.read_csv('ejemplos/ex1.csv')

En  [10]:  d.f.
Fuera[10]:
a b C mensaje
0  1 2 3 4  hola
15 6 7 8  mundo
2  9  10  11  12 Foo

También  podríamos  haber  usado  read_table  y  especificado  el  delimitador:

En  [11]:  pd.read_table('ejemplos/ex1.csv',  sep=',')
Fuera[11]:
a b C mensaje
0  1 2 3 4  hola
1  5  6  2  9   7 8  mundo
10  11  12 Foo

Un  archivo  no  siempre  tendrá  una  fila  de  encabezado.  Considere  este  archivo:

En  [12]: !cat  ejemplos/ex2.csv
1,2,3,4,  hola
5,6,7,8,mundo
9,10,11,12,foo

Para  leer  este  archivo,  tiene  un  par  de  opciones.  Puede  permitir  que  los  pandas  asignen  valores  predeterminados
nombres  de  columna,  o  puede  especificar  nombres  usted  mismo:

En  [13]:  pd.read_csv('ejemplos/ex2.csv',  encabezado=Ninguno)
Fuera[13]:
0  1 2 3  4
0  1 2 3 4  hola
15 6 7 8  mundo
2  9  10  11  12 Foo

En  [14]:  pd.read_csv('ejemplos/ex2.csv',  nombres=['a',  'b',  'c',  'd',  'mensaje'])
Fuera[14]:
a b C mensaje
0  1   2 3   4  hola
1  5 6 7 8  mundo
2  9  10  11  12 Foo

6.1  Leer  y  escribir  datos  en  formato  de  texto  |  169
Machine Translated by Google

Suponga  que  desea  que  la  columna  del  mensaje  sea  el  índice  del  DataFrame  devuelto.
Puede  indicar  que  desea  la  columna  en  el  índice  4  o  llamarla  'mensaje'  usando
el  argumento  index_col :

En  [15]:  nombres  =  ['a',  'b',  'c',  'd',  'mensaje']

En  [16]:  pd.read_csv('ejemplos/ex2.csv',  nombres=nombres,  index_col='mensaje')
Fuera[16]:
a  B  C  D
mensaje  
hola   1  2  3  4
mundo   5  6  7  8
foo 9  10  11  12

En  el  caso  de  que  desee  formar  un  índice  jerárquico  a  partir  de  varias  columnas,  pase  un
lista  de  números  de  columna  o  nombres:

En  [17]: !cat  ejemplos/csv_mindex.csv
clave1,clave2,valor1,valor2
uno,a,1,2
uno,b,3,4
uno,c,5,6
uno,d,7,8
dos,a,9,10
dos,b,11,12
dos,c,13,14
dos,  d,  15,  16

En  [18]:  analizado  =  pd.read_csv('ejemplos/csv_mindex.csv',
.....: index_col=['clave1',  'clave2'])

En  [19]:  analizado
Fuera[19]:
valor1  valor2
clave1  clave2
uno  un 1 2
b 3 4
5   6
cd 7 8
dos  abdominales 9 10
11 12
C 13 14
d 15 dieciséis

En  algunos  casos,  una  tabla  puede  no  tener  un  delimitador  fijo,  usando  espacios  en  blanco  o  algún  otro
otro  patrón  para  separar  campos.  Considere  un  archivo  de  texto  que  se  ve  así:

En  [20]:  list(open('ejemplos/ex3.txt'))
Fuera[20]:
[' A B C\n',
'aaa  ­0.264438  ­1.026059  ­0.619500\n',
'bbb  0.927272  0.302904  ­0.032399\n',

170  |  Capítulo  6:  Carga  de  datos,  almacenamiento  y  formatos  de  archivo
Machine Translated by Google

'ccc  ­0.264273  ­0.386314  ­0.217601\n',
'ddd  ­0.871858  ­0.348382  1.100491\n']

Si  bien  podría  hacer  algunos  cambios  a  mano,  los  campos  aquí  están  separados  por  una  variedad.
cantidad  capaz  de  espacios  en  blanco.  En  estos  casos,  puede  pasar  una  expresión  regular  como
delimitador  para  read_table.  Esto  se  puede  expresar  mediante  la  expresión  regular  \s+,  por  lo  que
tener  entonces:

En  [21]:  resultado  =  pd.read_table('ejemplos/ex3.txt',  sep='\s+')

En  [22]:  resultado
Fuera[22]:
A B C
aaa  ­0.264438  ­1.026059  ­0.619500
bbb  0.927272  0.302904  ­0.032399
ccc  ­0.264273  ­0.386314  ­0.217601
ddd  ­0.871858  ­0.348382  1.100491

Debido  a  que  había  un  nombre  de  columna  menos  que  el  número  de  filas  de  datos,

read_table  infiere  que  la  primera  columna  debe  ser  el  índice  de  DataFrame  en  este  espe
caso  especial.

Las  funciones  del  analizador  tienen  muchos  argumentos  adicionales  para  ayudarlo  a  manejar  la  amplia
variedad  de  formatos  de  archivo  de  excepción  que  ocurren  (vea  una  lista  parcial  en  la  Tabla  6­2).  Para
ejemplo,  puede  omitir  la  primera,  tercera  y  cuarta  fila  de  un  archivo  con  skiprows:

En  [23]: !cat  ejemplos/ex4.csv
#  ¡ey!
a,b,c,d,mensaje
#  solo  quería  ponerte  las  cosas  más  difíciles
#  ¿Quién  lee  los  archivos  CSV  con  las  computadoras?
1,2,3,4,  hola
5,6,7,8,mundo
9,10,11,12,foo
En  [24]:  pd.read_csv('ejemplos/ex4.csv',  skiprows=[0,  2,  3])
Fuera[24]:
a b C mensaje
0  1 2 3 4  hola
15 6 7 8  mundo
2  9  10  11  12 Foo

El  manejo  de  los  valores  faltantes  es  una  parte  importante  y  frecuentemente  matizada  de  los  parámetros  del  archivo.
proceso  de  ing  Los  datos  que  faltan  generalmente  no  están  presentes  (cadena  vacía)  o  están  marcados  por
algún  valor  centinela.  Por  defecto,  pandas  usa  un  conjunto  de  centinelas  comunes,
como  NA  y  NULL:

En  [25]: !cat  ejemplos/ex5.csv
algo,a,b,c,d,mensaje
uno,  1,  2,  3,  4,  NA
dos,  5,  6,  8,  mundo
tres,9,10,11,12,foo
En  [26]:  resultado  =  pd.read_csv('ejemplos/ex5.csv')

6.1  Leer  y  escribir  datos  en  formato  de  texto  |  171
Machine Translated by Google

En  [27]:  resultado
Fuera[27]:
algo  abd  mensaje C
0 uno  1  2  3.0  4  NaN
1 dos  5  6  NaN  8  mundo
2 tres  9  10  11.0  12  pies

En  [28]:  pd.isnull(resultado)
Fuera[28]:
algo  d  mensaje a b C
0 Falso  Falso  Falso  Falso  Falso  Verdadero
1 Falso  Falso  Falso  Verdadero  Falso  Falso
2 Falso  Falso  Falso  Falso  Falso FALSO

La  opción  na_values  puede  tomar  una  lista  o  un  conjunto  de  cadenas  para  considerar  que  faltan
valores:

En  [29]:  resultado  =  pd.read_csv('ejemplos/ex5.csv',  na_values=['NULL'])

En  [30]:  resultado
Fuera[30]:
algo  abd  mensaje C
uno  1  2  3.0  4  NaN
0  1 dos  5  6  NaN  8  mundo
2 tres  9  10  11.0  12  pies

Se  pueden  especificar  diferentes  centinelas  de  NA  para  cada  columna  en  un  dictado:

En  [31]:  centinelas  =  {'mensaje':  ['foo',  'NA'],  'algo':  ['dos']}

En  [32]:  pd.read_csv('ejemplos/ex5.csv',  na_values=centinelas)
Fuera[32]:
algo  abd  mensaje C
0 uno  1  2  3.0  4  NaN
1 NaN  5  6  NaN  8  mundo
2 tres  9  10  11.0  12 Yaya

La  Tabla  6­2  enumera  algunas  opciones  de  uso  frecuente  en  pandas.read_csv  y  pan
das.read_table.

Tabla  6­2.  Algunos  argumentos  de  la  función  read_csv/read_table

Argumento Descripción

ruta   Cadena  que  indica  la  ubicación  del  sistema  de  archivos,  URL  u  objeto  similar  a  un  archivo

separada  o  delimitador  Secuencia  de  caracteres  o  expresión  regular  que  se  usará  para  dividir  campos  en  cada  fila

encabezamiento Número  de  fila  para  usar  como  nombres  de  columna;  el  valor  predeterminado  es  0  (primera  fila),  pero  debe  ser  Ninguno  si  no  hay

fila  de  encabezado

index_col Números  de  columna  o  nombres  para  usar  como  índice  de  fila  en  el  resultado;  puede  ser  un  solo  nombre/número  o  un
lista  de  ellos  para  un  índice  jerárquico

nombres Lista  de  nombres  de  columna  para  resultado,  combinar  con  encabezado  =  Ninguno

172  |  Capítulo  6:  Carga  de  datos,  almacenamiento  y  formatos  de  archivo
Machine Translated by Google

Argumento Descripción

salteadores Número  de  filas  al  principio  del  archivo  para  ignorar  o  lista  de  números  de  fila  (a  partir  de  0)  para  omitir.

valores_na Secuencia  de  valores  para  reemplazar  con  NA.

comentario Carácter(es)  para  dividir  los  comentarios  al  final  de  las  líneas.

parse_dates Intente  analizar  los  datos  hasta  la  fecha  y  hora;  Falso  por  defecto.  Si  es  Verdadero,  intentará  analizar  todas  las  columnas.

De  lo  contrario,  puede  especificar  una  lista  de  números  de  columna  o  nombre  para  analizar.  Si  el  elemento  de  la  lista  es  una  tupla  o  una  lista,

combine  varias  columnas  y  analice  hasta  la  fecha  (por  ejemplo,  si  la  fecha/hora  se  divide  en  dos  columnas).

keep_date_col  Si  une  columnas  para  analizar  la  fecha,  mantenga  las  columnas  unidas;  Falso  por  defecto.

convertidores Dict  que  contiene  el  número  de  columna  del  mapeo  de  nombres  a  funciones  (por  ejemplo,  {'foo':  f}  aplicaría  el
función  f  a  todos  los  valores  en  la  columna  'foo' ).

primer  dia Al  analizar  fechas  potencialmente  ambiguas,  trátelas  como  formato  internacional  (p.  ej.,  7/6/2012  ­>  7  de  junio  de

2012);  Falso  por  defecto.

analizador_de_fechas Función  a  usar  para  analizar  fechas.

filas Número  de  filas  para  leer  desde  el  principio  del  archivo.

iterador Devuelve  un  objeto  TextParser  para  leer  el  archivo  por  partes.

tamaño  de  porción Para  la  iteración,  el  tamaño  de  los  fragmentos  de  archivo.

saltar_pie  de  página Número  de  líneas  a  ignorar  al  final  del  archivo.

verboso Imprima  diversa  información  de  salida  del  analizador,  como  la  cantidad  de  valores  faltantes  colocados  en  no  numéricos
columnas

codificación Codificación  de  texto  para  Unicode  (p.  ej.,  'utf­8'  para  texto  codificado  en  UTF­8).

estrujar Si  los  datos  analizados  solo  contienen  una  columna,  devuelva  una  Serie.

miles Separador  de  miles  (p.  ej.,  ','  o  '.').

Lectura  de  archivos  de  texto  en  partes

Al  procesar  archivos  muy  grandes  o  descubrir  el  conjunto  correcto  de  argumentos  para
procesar  correctamente  un  archivo  grande,  es  posible  que  solo  desee  leer  una  pequeña  parte  de  un  archivo  o  iterar
a  través  de  trozos  más  pequeños  del  archivo.

Antes  de  mirar  un  archivo  grande,  hacemos  que  la  configuración  de  visualización  de  pandas  sea  más  compacta:

En  [33]:  pd.options.display.max_rows  =  10

Ahora  tenemos:

En  [34]:  resultado  =  pd.read_csv('ejemplos/ex6.csv')

En  [35]:  resultado
Fuera[35]:
uno dos tres cuatro  teclas
0.467976  ­0.038649  ­0.295344  ­1.824726  L
0  1 ­0,358893  1,404453  0,704965  ­0,200638  B
2 ­0,501840  0,659254  ­0,421691  ­0,057688  G
3 0.204886  1.074134  1.388361  ­0.982404  R
4 0.354628  ­0.133116  0.283763  ­0.837063  Q
... ... ... ... ... ..
9995  2.311896  ­0.417070  ­1.409599  ­0.515821  L

6.1  Leer  y  escribir  datos  en  formato  de  texto  |  173
Machine Translated by Google

9996  ­0.479893  ­0.650419  0.745152  ­0.646038  E
9997  0.523331  0.787112  0.486066  1.093156  K
9998  ­0.362559  0.598894  ­1.843201  0.887292  G
9999  ­0.096376  ­1.012999  ­0.657431  ­0.573315  0
[10000  filas  x  5  columnas]

Si  solo  desea  leer  una  pequeña  cantidad  de  filas  (evitando  leer  todo  el  archivo),
especifica  eso  con  nrows:

En  [36]:  pd.read_csv('ejemplos/ex6.csv',  nrows=5)
Fuera[36]:
uno dos tres cuatro  teclas
0  0.467976  ­0.038649  ­0.295344  ­1.824726  L
1  ­0.358893  1.404453  0.704965  ­0.200638  B
2  ­0.501840  0.659254  ­0.421691  ­0.057688  G
3  0.204886  1.074134  1.388361  ­0.982404  R
4  0.354628  ­0.133116  0.283763  ­0.837063  Q

Para  leer  un  archivo  en  partes,  especifique  un  tamaño  de  fragmento  como  un  número  de  filas:

En  [37]:  chunker  =  pd.read_csv('examples/ex6.csv',  chunksize=1000)

En  [38]:  fragmentador
Salida[38]:  <pandas.io.parsers.TextFileReader  en  0x7f6b1e2672e8>

El  objeto  TextParser  devuelto  por  read_csv  le  permite  iterar  sobre  las  partes  de
el  archivo  de  acuerdo  con  el  tamaño  de  fragmento.  Por  ejemplo,  podemos  iterar  sobre  ex6.csv,  agregar
controlar  el  valor  cuenta  en  la  columna  'clave'  de  la  siguiente  manera:

chunker  =  pd.read_csv('ejemplos/ex6.csv',  tamaño  de  fragmento=1000)

tot  =  pd.Serie([])
por  pieza  en  chunker:
tot  =  tot.add(pieza['clave'].value_counts(),  fill_value=0)

tot  =  tot.sort_values(ascending=False)

Tenemos  entonces:

En  [40]:  total[:10]
Fuera[40]:
mi 368.0
X 364.0
L 346.0
O 343.0
q 340.0
METRO 338.0
j 337.0
F 335.0
k 334.0
H 330.0
tipo:  float64

174  |  Capítulo  6:  Carga  de  datos,  almacenamiento  y  formatos  de  archivo
Machine Translated by Google

TextParser  también  está  equipado  con  un  método  get_chunk  que  le  permite  leer  
piezas  de  un  tamaño  arbitrario.

Escritura  de  datos  en  formato  de  texto  Los  

datos  también  se  pueden  exportar  a  un  formato  delimitado.  Consideremos  uno  de  los  archivos  CSV  
leídos  antes:

En  [41]:  datos  =  pd.read_csv('ejemplos/ex5.csv')

In  [42]:  data  
Out[42]:  
algo  abd  mensaje  uno  1  2  3.0  
C 4  NaN  dos  5  6  
0 NaN  8  mundo  tres  9  10  11.0  12  foo
1
2

Usando  el  método  to_csv  de  DataFrame ,  podemos  escribir  los  datos  en  un  archivo  separado  por  comas:

En  [43]:  data.to_csv('ejemplos/out.csv')

En  [44]: !cat  ejemplos/
out.csv ,algo,a,b,c,d,mensaje  
0,uno,1,2,3.0,4,  
1,dos,5,6,,8,mundo  2,  
tres,9,10,11.0,12,foo

Por  supuesto,  se  pueden  usar  otros  delimitadores  (escribiendo  en  sys.stdout  para  que  imprima  el  
resultado  del  texto  en  la  consola):

En  [45]:  importar  sistema

En  [46]:  data.to_csv(sys.stdout,  sep='|')  |algo|a|b|c|d|
mensaje  0|uno|1|2|3.0|4|  1|dos|5|6||
8|mundo  2|tres|9|10|
11.0|12|foo

Los  valores  que  faltan  aparecen  como  cadenas  vacías  en  la  salida.  Es  posible  que  desee  indicarlos  
mediante  algún  otro  valor  centinela:

En  [47]:  data.to_csv(sys.stdout,  
na_rep='NULL') ,algo,a,b,c,d,mensaje  
0,uno,1,2,3.0,4,NULL  
1,dos,5,  6,NULO,8,mundo  
2,tres,9,10,11.0,12,foo

Sin  otras  opciones  especificadas,  se  escriben  las  etiquetas  de  fila  y  columna.  Ambos  se  pueden  
deshabilitar:

6.1  Leer  y  escribir  datos  en  formato  de  texto  |  175
Machine Translated by Google

En  [48]:  data.to_csv(sys.stdout,  index=False,  header=False)  one,1,2,3.0,4,  
two,5,6,,8,world  
three,9,10,11.0,12  
Foo

También  puede  escribir  solo  un  subconjunto  de  las  columnas  y  en  el  orden  que  elija:

En  [49]:  data.to_csv(sys.stdout,  índice=Falso,  columnas=['a',  'b',  'c'])  a,b,c  1,2,3.0  5,6,  
9,10  
,11.0

Series  también  tiene  un  método  to_csv :

En  [50]:  fechas  =  pd.date_range('1/1/2000',  periodos=7)

En  [51]:  ts  =  pd.Series(np.arange(7),  index=dates)

En  [52]:  ts.to_csv('ejemplos/tseries.csv')

En  [53]: !cat  ejemplos/tseries.csv  
2000­01­01,0  
2000­01­02,1  
2000­01­03,2  
2000­01­04,3  
2000­01­05,4  
2000­01  ­06,5  
2000­01­07,6

Trabajar  con  formatos  delimitados  Es  posible  cargar  

la  mayoría  de  las  formas  de  datos  tabulares  desde  el  disco  usando  funciones  como  pan  das.read_table.  En  
algunos  casos,  sin  embargo,  puede  ser  necesario  algún  procesamiento  manual.
No  es  raro  recibir  un  archivo  con  una  o  más  líneas  mal  formadas  que  tropiezan  con  read_table.  Para  ilustrar  las  
herramientas  básicas,  considere  un  pequeño  archivo  CSV:

En  [54]: !cat  ejemplos/ex7.csv  
"a","b","c"  "  
1","2","3"  
"1","2","3"

Para  cualquier  archivo  con  un  delimitador  de  un  solo  carácter,  puede  usar  el  módulo  csv  incorporado  de  Python .  
Para  usarlo,  pase  cualquier  archivo  abierto  u  objeto  similar  a  un  archivo  a  csv.reader:

importar  csv  
f  =  open('ejemplos/ex7.csv')

lector  =  csv.lector(f)

Iterar  a  través  del  lector  como  un  archivo  produce  tuplas  de  valores  con  cualquier  carácter  de  comillas  eliminado:

176  |  Capítulo  6:  Carga  de  datos,  almacenamiento  y  formatos  de  archivo
Machine Translated by Google

En  [56]:  para  línea  en  lector:  
.....: print(línea)  
['a',  'b',  'c']  ['1',  '2',  
'3']  ['1',  '2',  '  3']

A  partir  de  ahí,  depende  de  usted  hacer  las  gestiones  necesarias  para  poner  los  datos  en  la  forma  en  
que  los  necesita.  Vamos  a  tomar  esto  paso  a  paso.  Primero,  leemos  el  archivo  en  una  lista  de  líneas:

En  [57]:  with  open('examples/ex7.csv')  as  f:  lines  =  
.....: list(csv.reader(f))

Luego,  dividimos  las  líneas  en  la  línea  de  encabezado  y  las  líneas  de  datos:

En  [58]:  encabezado,  valores  =  líneas[0],  líneas[1:]

Luego,  podemos  crear  un  diccionario  de  columnas  de  datos  usando  una  comprensión  de  diccionario  y  la  
expresión  zip(*valores),  que  transpone  filas  a  columnas:

En  [59]:  data_dict  =  {h:  v  for  h,  v  in  zip(header,  zip(*values))}

En  [60]:  data_dict
Salida[60]:  {'a':  ('1',  '1'),  'b':  ('2',  '2'),  'c':  ('3',  '3')}

Los  archivos  CSV  vienen  en  muchos  sabores  diferentes.  Para  definir  un  nuevo  formato  con  un  delimitador  
diferente,  una  convención  de  comillas  de  cadena  o  un  terminador  de  línea,  definimos  una  subclase  simple  
de  csv.Dialect:

class  my_dialect(csv.Dialect):  
lineterminator  =  '\n'  
delimitador  =  ';'  
'"'
quotechar  =  
citando  =  csv.QUOTE_MINIMAL

lector  =  csv.reader(f,  dialecto=mi_dialecto)

También  podemos  dar  parámetros  de  dialecto  CSV  individuales  como  palabras  clave  a  csv.reader  sin  
tener  que  definir  una  subclase:

lector  =  csv.reader(f,  delimitador='|')

Las  opciones  posibles  (atributos  de  csv.Dialect)  y  lo  que  hacen  se  pueden  encontrar  en  la  Tabla  6­3.

Tabla  6­3.  Opciones  de  dialecto  CSV

Argumento Descripción  

delimitador Cadena  de  un  carácter  para  separar  campos;  el  valor  predeterminado  es  ','.

terminador  de  línea Terminador  de  línea  para  escribir;  por  defecto  es  '\r\n'.  Reader  ignora  esto  y  reconoce  los  terminadores  de  línea  multiplataforma.

cotizar Carácter  de  comillas  para  campos  con  caracteres  especiales  (como  un  delimitador);  el  valor  predeterminado  es  '"'.

6.1  Leer  y  escribir  datos  en  formato  de  texto  |  177
Machine Translated by Google

Argumento Descripción  

citando Convención  de  citas.  Las  opciones  incluyen  csv.QUOTE_ALL  (comillar  todos  los  campos),  csv.QUOTE_MINI  MAL  (solo  campos  con  

caracteres  especiales  como  el  delimitador),  csv.QUOTE_NONNUMERIC  y  csv.QUOTE_NONE  (sin  comillas).  Consulte  la  

documentación  de  Python  para  obtener  detalles  completos.  El  valor  predeterminado  es  QUOTE_MINIMAL.

skipinitialspace  Ignora  los  espacios  en  blanco  después  de  cada  delimitador;  el  valor  predeterminado  es  

Falso.  comillas  dobles Cómo  manejar  el  carácter  de  comillas  dentro  de  un  campo;  si  es  True,  se  duplica  (consulte  la  documentación  en  línea  para  conocer  

todos  los  detalles  y  el  comportamiento).

escapechar Cadena  para  escapar  del  delimitador  si  las  comillas  se  establecen  en  csv.QUOTE_NONE;  deshabilitado  por  defecto.

Para  archivos  con  delimitadores  de  varios  caracteres  fijos  o  más  
complicados,  no  podrá  utilizar  el  módulo  csv .  En  esos  casos,  deberá  
realizar  la  división  de  líneas  y  otras  tareas  de  limpieza  mediante  el  
método  de  división  de  cadenas  o  el  método  de  expresión  regular  re.split.

Para  escribir  archivos  delimitados  manualmente,  puede  usar  csv.writer.  Acepta  un  objeto  de  
archivo  abierto  y  grabable  y  las  mismas  opciones  de  formato  y  dialecto  que  csv.reader:

con  open('misdatos.csv',  'w')  como  f:  
escritor  =  csv.escritor(f,  dialecto=mi_dialecto)  
escritor.escritor(('uno',  'dos',  'tres'))  
escritor.escritor( ('1',  '2',  '3'))  
escritor.escritor(('4',  '5',  '6'))  
escritor.escritor(('7',  '8',  '9'))

Datos  JSON

JSON  (abreviatura  de  JavaScript  Object  Notation)  se  ha  convertido  en  uno  de  los  formatos  estándar  
para  enviar  datos  mediante  solicitud  HTTP  entre  navegadores  web  y  otras  aplicaciones.  Es  un  
formato  de  datos  mucho  más  libre  que  un  formulario  de  texto  tabular  como  CSV.  Aquí  hay  un  
ejemplo:
"""
obj  =  
{"nombre":  "Wes",  
"lugares_vivieron":  ["Estados  Unidos",  "España",  "Alemania"],  
"mascota":  
null,  "hermanos":  [{"nombre":  "Scott",  "edad":  30,  "mascotas":  ["Zeus",  "Zuko"]},  
{"nombre":  "Katie",  "edad":  38,  
"mascotas":  ["Sixes",  "Stache",  "  Cisco"]}]
}
"""

JSON  es  un  código  de  Python  casi  válido  con  la  excepción  de  su  valor  nulo  nulo  y  algunos  otros  
matices  (como  no  permitir  las  comas  finales  al  final  de  las  listas).  Los  tipos  básicos  son  objetos  
(dicts),  arreglos  (listas),  cadenas,  números,  valores  booleanos  y  nulos.  Todas  las  claves  de  un  
objeto  deben  ser  cadenas.  Hay  varias  bibliotecas  de  Python  para  leer

178  |  Capítulo  6:  Carga  de  datos,  almacenamiento  y  formatos  de  archivo
Machine Translated by Google

y  escribir  datos  JSON.  Usaré  json  aquí,  ya  que  está  integrado  en  la  biblioteca  estándar  de  Python.  
Para  convertir  una  cadena  JSON  a  forma  de  Python,  use  json.loads:

En  [62]:  importar  json

En  [63]:  resultado  =  json.loads(obj)

En  [64]:  resultado
Fuera[64]:  
{'nombre':  'Wes',  
'mascota':  
Ninguno,  'lugares_vividos':  ['Estados  Unidos',  'España',  'Alemania'],  
'hermanos':  [{'edad':  30 ,  'nombre':  'Scott',  'mascotas':  ['Zeus',  'Zuko']},  {'edad':  38,  
'nombre':  'Katie',  'mascotas':  ['Sixes',  'Stache  ',  'Cisco']}]}

json.dumps,  por  otro  lado,  convierte  un  objeto  de  Python  de  nuevo  a  JSON:

En  [65]:  asjson  =  json.dumps(resultado)

Usted  decide  cómo  convierte  un  objeto  JSON  o  una  lista  de  objetos  en  un  DataFrame  o  alguna  
otra  estructura  de  datos  para  el  análisis.  Convenientemente,  puede  pasar  una  lista  de  dictados  
(que  anteriormente  eran  objetos  JSON)  al  constructor  de  DataFrame  y  seleccionar  un  subconjunto  
de  los  campos  de  datos:

En  [66]:  hermanos  =  pd.DataFrame(resultado['hermanos'],  columnas=['nombre',  'edad'])

En  [67]:  hermanos
Fuera[67]:
nombre  Edad
0  Scott  30
1  Katie  38

pandas.read_json  puede  convertir  automáticamente  conjuntos  de  datos  JSON  en  arreglos  
específicos  en  una  serie  o  marco  de  datos .  Por  ejemplo:

En  [68]: !cat  ejemplos/ejemplo.json  [{"a":  1,  
"b":  2,  "c":  3},  {"a":  4,  "b":  5,  "c":  
6},  {"a":  7,  "b":  8,  "c":  9}]

Las  opciones  predeterminadas  para  pandas.read_json  asumen  que  cada  objeto  en  la  matriz  JSON  
es  una  fila  en  la  tabla:

En  [69]:  datos  =  pd.read_json('ejemplos/ejemplo.json')

En  [70]:  datos
Salida[70]:  
abc
0  1  2  3
1  4  5  6
2  7  8  9

6.1  Leer  y  escribir  datos  en  formato  de  texto  |  179
Machine Translated by Google

Para  obtener  un  ejemplo  ampliado  de  lectura  y  manipulación  de  datos  JSON  (incluidos  los  registros  anidados),  
consulte  el  ejemplo  de  la  base  de  datos  de  alimentos  del  USDA  en  el  Capítulo  7.

Si  necesita  exportar  datos  de  pandas  a  JSON,  una  forma  es  usar  los  métodos  to_json  en  Series  y  DataFrame:

En  [71]:  imprimir  (data.to_json())  {"a":
{"0":1,"1":4,"2":7},"b":{"0":2,"  1":5,"2":8},"c":{"0":3,"1":6,"2":9}}

En  [72]:  print(data.to_json(orient='registros'))  [{"a":1,"b":2,"c":3},
{"a":4,"b":  5,"c":6},{"a":7,"b":8,"c":9}]

XML  y  HTML:  Web  Scraping  Python  tiene  

muchas  bibliotecas  para  leer  y  escribir  datos  en  los  omnipresentes  formatos  HTML  y  XML.  Los  ejemplos  
incluyen  lxml,  Beautiful  Soup  y  html5lib.  Si  bien  lxml  es  comparativamente  mucho  más  rápido  en  general,  las  
otras  bibliotecas  pueden  manejar  mejor  los  archivos  HTML  o  XML  con  formato  incorrecto.

pandas  tiene  una  función  integrada,  read_html,  que  utiliza  bibliotecas  como  lxml  y  Beautiful  Soup  para  analizar  
automáticamente  las  tablas  de  los  archivos  HTML  como  objetos  DataFrame.  Para  mostrar  cómo  funciona  esto,  
descargué  un  archivo  HTML  (usado  en  la  documentación  de  pandas)  de  la  agencia  gubernamental  FDIC  de  los  
Estados  Unidos  que  muestra  fallas  bancarias.1  Primero,  debe  instalar  algunas  bibliotecas  adicionales  usadas  
por  read_html:

conda  instalar  lxml
pip  instalar  beautifulsoup4  html5lib

Si  no  está  utilizando  conda,  es  probable  que  pip  install  lxml  también  funcione.

La  función  pandas.read_html  tiene  varias  opciones,  pero  de  forma  predeterminada  busca  e  intenta  analizar  
todos  los  datos  tabulares  contenidos  en  las  etiquetas  <table> .  El  resultado  es  una  lista  de  objetos  DataFrame:

En  [73]:  tablas  =  pd.read_html('examples/fdic_failed_bank_list.html')

En  [74]:  len(tablas)
Salida[74]:  1

En  [75]:  fallas  =  tablas  [0]

En  [76]:  fallas.head()
Fuera[76]:
Ciudad  ST  CERT  \
Nombre  del  banco  

0 banco  aliado Mora  AR  91
1  La  empresa  bancaria  de  Woodbury  2 Woodbury  Georgia  11297
Primer  Banco  CornerStone  Rey  de  Prusia  PA  35312

1  Para  ver  la  lista  completa,  consulte  https://www.fdic.gov/bank/individual/failed/banklist.html.

180  |  Capítulo  6:  Carga  de  datos,  almacenamiento  y  formatos  de  archivo
Machine Translated by Google

3 Banco  de  la  compañía  fiduciaria Menfis  TN  9956
4 Banco  Estatal  del  Norte  de  Milwaukee Milwaukee  WI  20364
Institución  adquirente  0 Fecha  de  cierre Fecha  actualizada
Banco  de  hoy  23  de  septiembre  de  2016  17  de  noviembre  de  2016  1
banco  unido 19  de  agosto  de  2016  17  de  noviembre  de  2016
2  First­Citizens  Bank  &  Trust  Company 6  de  mayo  de  2016  6  de  septiembre  de  2016
El  banco  del  condado  de  Fayette 29  de  abril  de  2016  6  de  septiembre  de  2016  3
4  First­Citizens  Bank  &  Trust  Company 11  de  marzo  de  2016 16  de  junio  de  2016

Debido  a  que  fails  tiene  muchas  columnas,  pandas  inserta  un  carácter  de  salto  de  línea  \.

Como  aprenderá  en  capítulos  posteriores,  desde  aquí  podríamos  proceder  a  realizar  una  limpieza  
y  análisis  de  datos,  como  calcular  el  número  de  quiebras  bancarias  por  año:

En  [77]:  close_timestamps  =  pd.to_datetime(failures['Closing  Date'])

En  [78]:  close_timestamps.dt.year.value_counts()
Salida[78]:  
2010  157
2009 140
2011 92
2012 51
2008 25
...
2004 4
2001 4
2007 3
2003  3  2000  2

Nombre:  Fecha  de  cierre,  Longitud:  15,  dtype:  int64

Analizar  XML  con  lxml.objectify  

XML  (lenguaje  de  marcado  extensible)  es  otro  formato  de  datos  estructurados  común  que  admite  
datos  anidados  jerárquicos  con  metadatos.  El  libro  que  está  leyendo  actualmente  se  creó  a  partir  
de  una  serie  de  documentos  XML  de  gran  tamaño.

Anteriormente,  mostré  la  función  pandas.read_html ,  que  usa  lxml  o  Beautiful  Soup  bajo  el  capó  
para  analizar  datos  de  HTML.  XML  y  HTML  son  estructuralmente  similares,  pero  XML  es  más  
general.  Aquí,  mostraré  un  ejemplo  de  cómo  usar  lxml  para  analizar  datos  de  un  formato  XML  más  
general.

La  Autoridad  de  Transporte  Metropolitano  de  Nueva  York  (MTA)  publica  una  serie  de  datos  sobre  
sus  servicios  de  autobús  y  tren.  Aquí  veremos  los  datos  de  rendimiento,  que  se  encuentran  en  un  
conjunto  de  archivos  XML.  Cada  servicio  de  tren  o  autobús  tiene  un  archivo  diferente  (como  
Performance_MNR.xml  para  Metro­North  Railroad)  que  contiene  datos  mensuales  como  una  serie  
de  registros  XML  que  se  ven  así:

<INDICADOR>
<INDICATOR_SEQ>373889</INDICATOR_SEQ>
<PARENT_SEQ></PARENT_SEQ>

6.1  Leer  y  escribir  datos  en  formato  de  texto  |  181
Machine Translated by Google

<AGENCY_NAME>Metro­North  Railroad</AGENCY_NAME>  
<INDICATOR_NAME>  Disponibilidad  de  escaleras  mecánicas</
INDICATOR_NAME>  <DESCRIPTION>Porcentaje  de  tiempo  que  las  escaleras  
mecánicas  están  operativas  en  todo  el  sistema.  La  tasa  de  disponibilidad  se  basa  en  las  observaciones  
físicas  realizadas  únicamente  en  la  mañana  de  los  días  hábiles  normales.  Este  es  un  nuevo  indicador  que  
la  agencia  comenzó  a  informar  en  2009.</
DESCRIPTION>  <PERIOD_YEAR>2011</
PERIOD_YEAR>  <PERIOD_MONTH>12</
PERIOD_MONTH>  <CATEGORY>Service  Indicators</
CATEGORY>  <FREQUENCY>M</
FREQUENCY>  <  DESIRED_CHANGE>U</
DESIRED_CHANGE>  <INDICATOR_UNIT>%</
INDICATOR_UNIT>  <DECIMAL_LUGARES>1</
DECIMAL_LUGARES>  
<YTD_TARGET>97.00</
YTD_TARGET>  <YTD_ACTUAL></YTD_ACTUAL>  
<MONTHLY_TARGET>97.00</
MONTHLY_TARGET>  <MONTHLY_ACTUAL>< /MENSUAL_ACTUAL>  </INDICADOR>

Usando  lxml.objectify,  analizamos  el  archivo  y  obtenemos  una  referencia  al  nodo  raíz  del  archivo  XML  con  
getroot:

desde  lxml  import  objectify

ruta  =  'ejemplos/mta_perf/Performance_MNR.xml'  analizado  =  
objetificar.parse(abrir(ruta))  root  =  analizado.getroot()

root.INDICATOR  devuelve  un  generador  que  produce  cada  elemento  XML  <INDICATOR> .  Para  cada  
registro,  podemos  completar  un  dictado  de  nombres  de  etiquetas  (como  YTD_ACTUAL)  a  valores  de  datos  
(excluyendo  algunas  etiquetas):

datos  =  []

skip_fields  =  ['PARENT_SEQ',  'INDICATOR_SEQ',
'CAMBIO_DESEADO',  'LUGARES_DECIMALES']

for  elt  en  root.INDICATOR:  el_data  
=  {}  for  child  en  
elt.getchildren():  if  child.tag  en  skip_fields:

continue  
el_data[child.tag]  =  child.pyval  
data.append(el_data)

Por  último,  convierta  esta  lista  de  dictados  en  un  DataFrame:

En  [81]:  rendimiento  =  pd.DataFrame(datos)

En  [82]:  perf.head()
Out[82]:  
Trama  de  datos  vacía

182  |  Capítulo  6:  Carga  de  datos,  almacenamiento  y  formatos  de  archivo
Machine Translated by Google

Columnas:  []
Índice:  []

Los  datos  XML  pueden  volverse  mucho  más  complicados  que  este  ejemplo.  Cada  etiqueta  también  puede  tener  
metadatos.  Considere  una  etiqueta  de  enlace  HTML,  que  también  es  XML  válido:

from  io  import  StringIO  tag  =  '<a  
href="http://www.google.com">Google</a>'  root  =  
objectify.parse(StringIO(tag)).getroot()

Ahora  puede  acceder  a  cualquiera  de  los  campos  (como  href)  en  la  etiqueta  o  el  texto  del  enlace:

En  [84]:  raíz
Salida[84]:  <Elemento  a  en  0x7f6b15817748>

En  [85]:  root.get('href')
Salida[85]:  'http://www.google.com'

Entrada  [86]:  root.text  
Salida  [86]:  'Google'

6.2  Formatos  de  datos  binarios
Una  de  las  formas  más  fáciles  de  almacenar  datos  (también  conocida  como  serialización)  de  manera  eficiente  en  
formato  binario  es  usar  la  serialización  pickle  integrada  de  Python .  Todos  los  objetos  pandas  tienen  un  método  
to_pickle  que  escribe  los  datos  en  el  disco  en  formato  pickle:

En  [87]:  cuadro  =  pd.read_csv('ejemplos/ex1.csv')

En  [88]:  marco
Salida[88]:  
a b C d  mensaje  4  
0  1 2 3 hola
1  5  6  2  9   7 8  mundo  foo
10  11  12

En  [89]:  frame.to_pickle('ejemplos/frame_pickle')

Puede  leer  cualquier  objeto  "decapado"  almacenado  en  un  archivo  usando  el  pickle  incorporado  directamente,  o  
incluso  más  convenientemente  usando  pandas.read_pickle:

En  [90]:  pd.read_pickle('ejemplos/frame_pickle')
Salida[90]:  
a b C d  mensaje  4  
0  1 2 3 hola
1  5  6  2  9   7 8  mundo  foo
10  11  12

6.2  Formatos  de  datos  binarios  |  183
Machine Translated by Google

pickle  solo  se  recomienda  como  formato  de  almacenamiento  a  corto  plazo.  El  
problema  es  que  es  difícil  garantizar  que  el  formato  sea  estable  en  el  tiempo;  
es  posible  que  un  objeto  decapado  hoy  no  se  deshaga  con  una  versión  
posterior  de  una  biblioteca.  Hemos  tratado  de  mantener  la  compatibilidad  con  
versiones  anteriores  siempre  que  sea  posible,  pero  en  algún  momento  en  el  
futuro  puede  ser  necesario  "romper"  el  formato  pickle.

pandas  tiene  soporte  integrado  para  dos  formatos  de  datos  binarios  más:  HDF5  y  Message­Pack.  Daré  
algunos  ejemplos  de  HDF5  en  la  siguiente  sección,  pero  lo  animo  a  explorar  diferentes  formatos  de  
archivo  para  ver  qué  tan  rápidos  son  y  qué  tan  bien  funcionan  para  su  análisis.  Algunos  otros  formatos  
de  almacenamiento  para  pandas  o  datos  NumPy  incluyen:

bcolz  
Un  formato  binario  comprimible  orientado  a  columnas  basado  en  la  biblioteca  de  compresión  
Blosc.

Feather  
Un  formato  de  archivo  multilenguaje  orientado  a  columnas  que  diseñé  con  Hadley  Wickham  de  la  
comunidad  de  programación  R.  Feather  utiliza  el  formato  de  memoria  en  columnas  Apache  Arrow .

Uso  del  formato  HDF5  HDF5  

es  un  formato  de  archivo  bien  considerado  diseñado  para  almacenar  grandes  cantidades  de  datos  de  
matrices  científicas.  Está  disponible  como  biblioteca  C  y  tiene  interfaces  disponibles  en  muchos  otros  
lenguajes,  incluidos  Java,  Julia,  MATLAB  y  Python.  El  "HDF"  en  HDF5  significa  formato  de  datos  
jerárquicos.  Cada  archivo  HDF5  puede  almacenar  múltiples  conjuntos  de  datos  y  metadatos  de  apoyo.  
En  comparación  con  formatos  más  simples,  HDF5  admite  la  compresión  sobre  la  marcha  con  una  
variedad  de  modos  de  compresión,  lo  que  permite  que  los  datos  con  patrones  repetidos  se  almacenen  
de  manera  más  eficiente.  HDF5  puede  ser  una  buena  opción  para  trabajar  con  conjuntos  de  datos  muy  
grandes  que  no  caben  en  la  memoria,  ya  que  puede  leer  y  escribir  eficientemente  pequeñas  secciones  
de  arreglos  mucho  más  grandes.

Si  bien  es  posible  acceder  directamente  a  los  archivos  HDF5  utilizando  las  bibliotecas  PyTables  o  h5py,  
pandas  proporciona  una  interfaz  de  alto  nivel  que  simplifica  el  almacenamiento  de  objetos  Series  y  
DataFrame.  La  clase  HDFStore  funciona  como  un  dict  y  maneja  los  detalles  de  bajo  nivel:

En  [92]:  cuadro  =  pd.DataFrame({'a':  np.random.randn(100)})

En  [93]:  tienda  =  pd.HDFStore('misdatos.h5')

En  [94]:  tienda['obj1']  =  marco

En  [95]:  tienda['obj1_col']  =  marco['a']

En  [96]:  almacenar

184  |  Capítulo  6:  Carga  de  datos,  almacenamiento  y  formatos  de  archivo
Machine Translated by Google

Salida[96]:  
<clase  'pandas.io.pytables.HDFStore'>  Ruta  del  
archivo:  mydata.h5 /obj1
marco (forma­>[100,1])

/obj1_col serie (forma­>[100])

/obj2   frame_table  (typ­>appendable,nrows­>100,ncols­>1,indexers­>
[índice]) /
obj3   frame_table  (typ­>appendable,nrows­>100,ncols­>1,indexers­>
[índice])

Los  objetos  contenidos  en  el  archivo  HDF5  se  pueden  recuperar  con  la  misma  API  similar  a  dict:

En  [97]:  almacenar['obj1']
Fuera[97]:
a
0  ­0.204708
1  0,478943  2  
­0,519439
3  ­0.555730
4  1.965781
.. ...
95  0,795253  96  
0,118110
97  ­0.748532
98  0.584970
99  0.152677
[100  filas  x  1  columna]

HDFStore  admite  dos  esquemas  de  almacenamiento,  'fijo'  y  'tabla'.  Este  último  es  generalmente  más  
lento,  pero  admite  operaciones  de  consulta  utilizando  una  sintaxis  especial:

En  [98]:  store.put('obj2',  cuadro,  formato='tabla')

En  [99]:  store.select('obj2',  where=['index  >=  10  and  index  <=  15'])
Fuera[99]:
a
10  1,007189  11  
­1,296221
12  0.274992
13  0.228913
14  1.352917
15  0.886429

En  [100]:  tienda.cerrar()

El  put  es  una  versión  explícita  del  método  store['obj2']  =  frame  pero  nos  permite  establecer  otras  
opciones  como  el  formato  de  almacenamiento.

La  función  pandas.read_hdf  le  brinda  un  acceso  directo  a  estas  herramientas:

En  [101]:  frame.to_hdf('mydata.h5',  'obj3',  format='table')

6.2  Formatos  de  datos  binarios  |  185
Machine Translated by Google

En  [102]:  pd.read_hdf('mydata.h5',  'obj3',  where=['index  <  5'])
Fuera[102]:
a
0  ­0.204708
1  0.478943
2  ­0.519439
3  ­0.555730
4  1.965781

Si  está  procesando  datos  almacenados  en  servidores  remotos,  como
Amazon  S3  o  HDFS,  utilizando  un  formato  binario  diferente  diseñado  para
el  almacenamiento  distribuido  como  Apache  Parquet  puede  ser  más  adecuado.
Python  para  Parquet  y  otros  formatos  de  almacenamiento  similares  aún  se  están  desarrollando.
ing,  por  lo  que  no  escribo  sobre  ellos  en  este  libro.

Si  trabaja  con  grandes  cantidades  de  datos  localmente,  le  animo  a  explorar
PyTables  y  h5py  para  ver  cómo  pueden  adaptarse  a  sus  necesidades.  Dado  que  muchos  análisis  de  datos
los  problemas  están  vinculados  a  E/S  (en  lugar  de  vinculados  a  la  CPU),  el  uso  de  una  herramienta  como  HDF5  puede  combinar
acelere  sus  aplicaciones  de  manera  significativa.

HDF5  no  es  una  base  de  datos.  Es  más  adecuado  para  escribir  una  vez,  leer  muchas
conjuntos  de  datos  Si  bien  se  pueden  agregar  datos  a  un  archivo  en  cualquier  momento,  si  hay  varios

los  escritores  lo  hacen  simultáneamente,  el  archivo  puede  corromperse.

Lectura  de  archivos  de  Microsoft  Excel

pandas  también  admite  la  lectura  de  datos  tabulares  almacenados  en  archivos  de  Excel  2003  (y  superior)
utilizando  la  clase  ExcelFile  o  la  función  pandas.read_excel .  Internamente  estos
Las  herramientas  utilizan  los  paquetes  complementarios  xlrd  y  openpyxl  para  leer  archivos  XLS  y  XLSX,  respectivamente.
tivamente.  Es  posible  que  deba  instalarlos  manualmente  con  pip  o  conda.

Para  usar  ExcelFile,  cree  una  instancia  pasando  una  ruta  a  un  archivo  xls  o  xlsx :

En  [104]:  xlsx  =  pd.ExcelFile('ejemplos/ex1.xlsx')

Los  datos  almacenados  en  una  hoja  se  pueden  leer  en  DataFrame  con  parse:

En  [105]:  pd.read_excel(xlsx,  'Hoja1')
Fuera[105]:
mensaje
un  0  1 do  3
segundo  24  hola
15 6 7 8  mundo
2  9  10  11  12 Foo

Si  está  leyendo  varias  hojas  en  un  archivo,  entonces  es  más  rápido  crear  el  ExcelFile,
pero  también  puede  simplemente  pasar  el  nombre  del  archivo  a  pandas.read_excel:

186  |  Capítulo  6:  Carga  de  datos,  almacenamiento  y  formatos  de  archivo
Machine Translated by Google

En  [106]:  marco  =  pd.read_excel('ejemplos/ex1.xlsx',  'Hoja1')

En  [107]:  cuadro
Fuera[107]:
a b C mensaje
0  1 2 3 4  hola
15 6 7 8  mundo
2  9  10  11  12 Foo

Para  escribir  datos  de  pandas  en  formato  Excel,  primero  debe  crear  un  ExcelWriter,  luego
escriba  datos  en  él  usando  el  método  to_excel  de  pandas  objects :

En  [108]:  escritor  =  pd.ExcelWriter('ejemplos/ex2.xlsx')

En  [109]:  frame.to_excel(escritor,  'Hoja1')

En  [110]:  escritor.guardar()

También  puede  pasar  una  ruta  de  archivo  a  to_excel  y  evitar  el  ExcelWriter:

En  [111]:  frame.to_excel('ejemplos/ex2.xlsx')

6.3  Interacción  con  API  web
Muchos  sitios  web  tienen  API  públicas  que  proporcionan  fuentes  de  datos  a  través  de  JSON  o  algún  otro  formato.
Hay  varias  formas  de  acceder  a  estas  API  desde  Python;  uno  fácil  de  usar
El  método  que  recomiendo  es  el  paquete  de  solicitudes .

Para  encontrar  los  últimos  30  problemas  de  GitHub  para  pandas  en  GitHub,  podemos  hacer  un  GET  HTTP
solicitud  usando  la  biblioteca  de  solicitudes  de  complementos :

In  [113]:  solicitudes  de  importación

En  [114]:  url  =  'https://api.github.com/repos/pandas­dev/pandas/issues'

En  [115]:  resp  =  solicitudes.get(url)

En  [116]:  resp.
Salida[116]:  <Respuesta  [200]>

El  método  json  del  objeto  Response  devolverá  un  diccionario  que  contiene  JSON  analizado
en  objetos  Python  nativos:

En  [117]:  datos  =  resp.json()

En  [118]:  datos[0]['título']
Out[118]:  'El  período  no  se  redondea  hacia  abajo  para  frecuencias  menores  a  1  hora'

Cada  elemento  de  los  datos  es  un  diccionario  que  contiene  todos  los  datos  que  se  encuentran  en  un  GitHub
página  del  problema  (excepto  los  comentarios).  Podemos  pasar  datos  directamente  a  DataFrame  y
extraer  campos  de  interés:

6.3  Interacción  con  las  API  web  |  187
Machine Translated by Google

En  [119]:  problemas  =  pd.DataFrame  (datos,  columnas  =  ['número',  'título',
.....: 'etiquetas',  'estado'])

En  [120]:  problemas
Salida[120]:
título  del  número  \
0 17666  El  período  no  se  redondea  hacia  abajo  para  frecuencias  menos...
1 donde   DOC:  mejore  la  cadena  de  documentación  de  la  función  
2   17665  17664  COMPAT:  omita  la  prueba  de  32  bits  en  int  repr  17662  implemente  
3 la  clase  Delegator  17654  ERROR:  corrija  el  cambio  de  nombre  de  la  serie  
4 llamado  con  str  alterin ...
.. ... ...
25  17603  ERROR:  Localizar  correctamente  cadenas  de  fecha  y  hora  
ingenuas...  26  17599  core.dtypes.generic  ­­>  cython  27  17596  Fusionar  la  
funcionalidad  cdate_range  en  bdate_range  28  17587  Corrección  de  errores  
en  el  agrupador  de  tiempo  cuando  se  aplica  para  list  gro...
29  17583  ERROR:  corregir  tz­aware  DatetimeIndex  +  TimedeltaInd...
estado  de  las  etiquetas

0  []  abrir  1  [{'id':  134699,  'url':  'https://api.github.com...  abrir  2  [{'id':  563047854,  
'url':  'https://api .github....  abierto  []  abierto  3  4  [{'id':  76811,  'url':  'https://
api.github.com/...  abierto

.. ... ...
25  [{'id':  76811,  'url':  'https://api.github.com/...  abierto  26  [{'id':  49094459,  'url':  
'https://api.github.  c...  abrir  27  [{'id':  35818298,  'url':  'https://api.github.c...  abrir  
28  [{'id':  233160,  'url':  'https://  api.github.com...  abrir  29  [{'id':  76811,  'url':  'https://
api.github.com/...  abrir  [30  filas  x  4  columnas]

Con  un  poco  de  esfuerzo,  puede  crear  algunas  interfaces  de  nivel  superior  para
API  web  que  devuelven  objetos  DataFrame  para  facilitar  el  análisis.

6.4  Interactuando  con  bases  de  datos
En  un  entorno  empresarial,  es  posible  que  la  mayoría  de  los  datos  no  se  almacenen  en  archivos  de  texto  o  de  Excel.  basado  en  SQL

Las  bases  de  datos  relacionales  (como  SQL  Server,  PostgreSQL  y  MySQL)  se  utilizan  ampliamente,
y  muchas  bases  de  datos  alternativas  se  han  vuelto  muy  populares.  La  elección  de  la  base  de  datos  es
generalmente  depende  del  rendimiento,  la  integridad  de  los  datos  y  las  necesidades  de  escalabilidad  de  un
solicitud.

Cargar  datos  de  SQL  en  un  DataFrame  es  bastante  sencillo  y  pandas  tiene
algunas  funciones  para  simplificar  el  proceso.  Como  ejemplo,  crearé  una  base  de  datos  SQLite
usando  el  controlador  sqlite3  incorporado  de  Python :

En  [121]:  importar  sqlite3

"""
En  [122]:  consulta  
= .....:  prueba  CREAR  TABLA

188  |  Capítulo  6:  Carga  de  datos,  almacenamiento  y  formatos  de  archivo
Machine Translated by Google

.....:  (a  VARCHAR(20),  b  VARCHAR(20),  d  
ENTERO .....:  c  
REAL, .....: );"""

En  [123]:  con  =  sqlite3.connect('misdatos.sqlite')

En  [124]:  con.ejecutar(consultar)
Salida[124]:  <sqlite3.Cursor  en  0x7f6b12a50f10>

En  [125]:  con.commit()

Luego,  inserte  algunas  filas  de  datos:

En  [126]:  datos  =  [('Atlanta',  'Georgia',  1.25,  6),
.....: ('Tallahassee',  'Florida',  2.6,  3),  ('Sacramento',  
.....: 'California',  1.7,  5)]

En  [127]:  sentencia  =  "INSERTAR  EN  VALORES  de  prueba  (?, ?, ?, ?)"

En  [128]:  con.executemany(stmt,  data)
Salida[128]:  <sqlite3.Cursor  en  0x7f6b15c66ce0>

En  [129]:  con.commit()

La  mayoría  de  los  controladores  Python  SQL  (PyODBC,  psycopg2,  MySQLdb,  pymssql,  etc.)  devuelven  
una  lista  de  tuplas  al  seleccionar  datos  de  una  tabla:

En  [130]:  cursor  =  con.execute('seleccionar  *  de  la  prueba')

En  [131]:  filas  =  cursor.fetchall()

Entrada  [132]:  
filas  
Salida[132]:  [('Atlanta',  'Georgia',  1.25,  6),  
('Tallahassee',  'Florida',  2.6,  3),  ('Sacramento',  
'California',  1.7 ,  5)]

Puede  pasar  la  lista  de  tuplas  al  constructor  de  DataFrame,  pero  también  necesita  los  nombres  de  las  
columnas,  contenidos  en  el  atributo  de  descripción  del  cursor :

In  [133]:  cursor.description  Out[133]:  
(('a',  
Ninguno,  Ninguno,  Ninguno,  Ninguno,  Ninguno,  Ninguno),  
('b',  Ninguno,  Ninguno,  Ninguno,  Ninguno,  Ninguno,  
Ninguno),  ('c',  Ninguno,  Ninguno,  Ninguno,  Ninguno,  
Ninguno,  Ninguno),  ('d',  Ninguno,  Ninguno,  Ninguno,  Ninguno,  Ninguno,  Ninguno))

En  [134]:  pd.DataFrame(filas,  columnas=[x[0]  para  x  en  cursor.descripción])
Fuera[134]:
a b cd
0 atlanta Georgia  1,25  6  1  
Tallahassee  Florida  2,60  3  2  Sacramento  
California  1,70  5

6.4  Interactuar  con  bases  de  datos  |  189
Machine Translated by Google

Esto  es  bastante  complicado  y  preferiría  no  repetirlo  cada  vez  que  consulta  la  base  de  datos.  El  
proyecto  SQLAlchemy  es  un  popular  conjunto  de  herramientas  Python  SQL  que  abstrae  muchas  
de  las  diferencias  comunes  entre  las  bases  de  datos  SQL.  pandas  tiene  una  función  read_sql  que  
le  permite  leer  datos  fácilmente  desde  una  conexión  SQLAlchemy  general.  Aquí,  nos  conectaremos  
a  la  misma  base  de  datos  SQLite  con  SQLAlchemy  y  leeremos  los  datos  de  la  tabla  creada  antes:

En  [135]:  importar  sqlalchemy  como  sqla

En  [136]:  db  =  sqla.create_engine('sqlite:///mydata.sqlite')

En  [137]:  pd.read_sql('seleccionar  *  de  prueba',  db)
Fuera[137]:
a b cd
0  Atlanta  Georgia  1,25  6  1  Tallahassee  
Florida  2,60  3  2  Sacramento  California  1,70  
5

6.5  Conclusión
Con  frecuencia,  obtener  acceso  a  los  datos  es  el  primer  paso  en  el  proceso  de  análisis  de  datos.  
Hemos  visto  una  serie  de  herramientas  útiles  en  este  capítulo  que  deberían  ayudarlo  a  comenzar.  
En  los  próximos  capítulos,  profundizaremos  en  la  disputa  de  datos,  la  visualización  de  datos,  el  
análisis  de  series  temporales  y  otros  temas.

190  |  Capítulo  6:  Carga  de  datos,  almacenamiento  y  formatos  de  archivo
Machine Translated by Google

CAPÍTULO  7

Limpieza  y  preparación  de  datos

Durante  el  transcurso  del  análisis  y  el  modelado  de  datos,  se  dedica  una  cantidad  significativa  de  tiempo  a  
la  preparación  de  datos:  carga,  limpieza,  transformación  y  reorganización.  A  menudo  se  informa  que  tales  
tareas  ocupan  el  80%  o  más  del  tiempo  de  un  analista.  A  veces,  la  forma  en  que  se  almacenan  los  datos  
en  archivos  o  bases  de  datos  no  tiene  el  formato  correcto  para  una  tarea  en  particular.  Muchos  
investigadores  optan  por  realizar  un  procesamiento  ad  hoc  de  datos  de  un  formulario  a  otro  utilizando  un  
lenguaje  de  programación  de  propósito  general,  como  Python,  Perl,  R  o  Java,  o  herramientas  de  
procesamiento  de  texto  de  Unix  como  sed  o  awk.  Afortunadamente,  pandas,  junto  con  las  características  
integradas  del  lenguaje  Python,  le  proporciona  un  conjunto  de  herramientas  rápido,  flexible  y  de  alto  nivel  
que  le  permite  manipular  los  datos  en  la  forma  correcta.

Si  identifica  un  tipo  de  manipulación  de  datos  que  no  se  encuentra  en  este  libro  ni  en  ninguna  otra  parte  de  
la  biblioteca  de  pandas,  siéntase  libre  de  compartir  su  caso  de  uso  en  una  de  las  listas  de  correo  de  Python  
o  en  el  sitio  de  pandas  en  GitHub.  De  hecho,  gran  parte  del  diseño  y  la  implementación  de  pandas  ha  sido  
impulsado  por  las  necesidades  de  las  aplicaciones  del  mundo  real.

En  este  capítulo  analizo  herramientas  para  datos  faltantes,  datos  duplicados,  manipulación  de  cadenas  y  
algunas  otras  transformaciones  de  datos  analíticos.  En  el  próximo  capítulo,  me  enfoco  en  combinar  y  
reorganizar  conjuntos  de  datos  de  varias  maneras.

7.1  Manejo  de  datos  faltantes
La  falta  de  datos  ocurre  comúnmente  en  muchas  aplicaciones  de  análisis  de  datos.  Uno  de  los  objetivos  
de  pandas  es  hacer  que  trabajar  con  datos  faltantes  sea  lo  menos  doloroso  posible.  Por  ejemplo,  todas  las  
estadísticas  descriptivas  de  los  objetos  pandas  excluyen  los  datos  faltantes  de  forma  predeterminada.

La  forma  en  que  se  representan  los  datos  faltantes  en  los  objetos  pandas  es  algo  imperfecta,  pero  es  
funcional  para  muchos  usuarios.  Para  datos  numéricos,  pandas  usa  el  valor  de  coma  flotante  NaN  (No  es  
un  número)  para  representar  los  datos  que  faltan.  Llamamos  a  esto  un  valor  centinela  que  se  puede  
detectar  fácilmente:

191
Machine Translated by Google

En  [10]:  string_data  =  pd.Series(['aardvark',  'alcachofa',  np.nan,  'aguacate'])

En  [11]:  cadena_datos
Fuera[11]:
0  oso  hormiguero

1 alcachofa
2 Yaya
3 palta
tipo:  objeto

En  [12]:  string_data.isnull()
Fuera[12]:
0 FALSO
1   FALSO
2 Verdadero

3 FALSO
dtipo:  booleano

En  pandas,  hemos  adoptado  una  convención  utilizada  en  el  lenguaje  de  programación  R  por  referencia
llamar  a  los  datos  faltantes  como  NA,  que  significa  no  disponible.  En  las  aplicaciones  de  estadísticas,  los  datos  
NA  pueden  ser  datos  que  no  existen  o  que  existen  pero  no  se  observaron
(a  través  de  problemas  con  la  recopilación  de  datos,  por  ejemplo).  Al  limpiar  los  datos  de
análisis,  a  menudo  es  importante  hacer  un  análisis  de  los  datos  que  faltan  para  identificar  los  datos
problemas  de  recopilación  o  posibles  sesgos  en  los  datos  causados  por  datos  faltantes.

El  valor  integrado  de  Python  Ninguno  también  se  trata  como  NA  en  las  matrices  de  objetos:

En  [13]:  string_data[0]  =  Ninguno

En  [14]:  string_data.isnull()
Fuera[14]:
0 Verdadero

1 FALSO
2 Verdadero

3 FALSO
dtipo:  booleano

Se  está  trabajando  en  el  proyecto  pandas  para  mejorar  los  detalles  internos  de  cómo
los  datos  faltantes  se  manejan,  pero  las  funciones  de  la  API  del  usuario,  como  pandas.isnull,  abstraen  muchos  
de  los  detalles  molestos.  Consulte  la  Tabla  7­1  para  obtener  una  lista  de  algunas  funciones  relacionadas
al  manejo  de  datos  perdidos.

Tabla  7­1.  Métodos  de  manejo  NA

Argumento  Descripción

dropna  Filtre  las  etiquetas  de  los  ejes  en  función  de  si  los  valores  de  cada  etiqueta  tienen  datos  faltantes,  con  diferentes  umbrales  de  cómo
muchos  datos  perdidos  que  tolerar.

fillna  Rellene  los  datos  faltantes  con  algún  valor  o  utilizando  un  método  de  interpolación  como  'ffill'  o  'bfill'.

isnull  Devuelve  valores  booleanos  que  indican  qué  valores  faltan/NA.

notnull  Negación  de  isnull.

192  |  Capítulo  7:  Limpieza  y  preparación  de  datos
Machine Translated by Google

Filtrado  de  datos  faltantes
Hay  algunas  formas  de  filtrar  los  datos  que  faltan.  Si  bien  siempre  tiene  la  opción  de
hágalo  a  mano  usando  pandas.isnull  y  la  indexación  booleana,  el  dropna  puede  ser  útil.
En  una  Serie,  devuelve  la  Serie  con  solo  los  datos  no  nulos  y  los  valores  de  índice:

En  [15]:  from  numpy  import  nan  as  NA

En  [16]:  datos  =  pd.Series([1,  NA,  3.5,  NA,  7])

En  [17]:  data.dropna()
Fuera[17]:
0  1.0
2  3.5
7.0
4  tipo:  float64

Esto  es  equivalente  a:

En  [18]:  datos[datos.notnull()]
Fuera[18]:
0  1.0
2 3.5
4  7.0
tipo:  float64

Con  los  objetos  DataFrame,  las  cosas  son  un  poco  más  complejas.  Es  posible  que  desee  soltar  filas
o  columnas  que  son  todas  NA  o  solo  aquellas  que  contienen  NA.  dropna  por  defecto  gotas
cualquier  fila  que  contenga  un  valor  faltante:

En  [19]:  datos  =  pd.DataFrame([[1.,  6.5,  3.],  [1.,  NA,  NA],
.....: [NA,  NA,  NA],  [NA,  6.5,  3.]])

En  [20]:  limpiado  =  data.dropna()

En  [21]:  datos
Fuera[21]:
0 1 2
0  1,0  6,5  3,0
1  1,0  NaN  NaN
2  NaN  NaN  NaN
3  NaN  6,5  3,0

En  [22]:  limpiado
Fuera[22]:
0 1 2
0  1,0  6,5  3,0

Pasar  how='all'  solo  eliminará  las  filas  que  son  todas  NA:

En  [23]:  data.dropna(how='all')
Fuera[23]:
0 1 2

7.1  Manejo  de  datos  faltantes  |  193
Machine Translated by Google

0  1,0  6,5  3,0
1  1,0  NaN  NaN
3  NaN  6,5  3,0

Para  soltar  columnas  de  la  misma  manera,  pase  axis=1:

En  [24]:  datos[4]  =  NA

En  [25]:  datos
Fuera[25]:
1 2 4
0  0  1,0  6,5  3,0  NaN
1  1,0  NaN  NaN  NaN
2  NaN  NaN  NaN  NaN
3  NaN  6,5  3,0  NaN

En  [26]:  data.dropna(axis=1,  how='all')
Fuera[26]:
0 1 2
0  1,0  6,5  3,0
1  1,0  NaN  NaN
2  NaN  NaN  NaN
3  NaN  6,5  3,0

Una  forma  relacionada  de  filtrar  filas  de  DataFrame  tiende  a  referirse  a  datos  de  series  temporales.  Suponer
desea  mantener  solo  filas  que  contengan  un  cierto  número  de  observaciones.  Puede
indicar  esto  con  el  argumento  de  umbral :

En  [27]:  df  =  pd.DataFrame(np.random.randn(7,  3))

En  [28]:  df.iloc[:4,  1]  =  NA

En  [29]:  df.iloc[:2,  2]  =  NA

En  [30]:  d.f.
Fuera[30]:
0 1 2
0  ­0.204708 Yaya Yaya
1  ­0.555730  2   NaN  NaN
0.092908 NaN  0,769023
3  1.246435 NaN  ­1.296221
4  0.274992  0.228913  1.352917
5  0,886429  ­2,001637  ­0,371843
6  1.669025  ­0.438570  ­0.539741

En  [31]:  df.dropna()
Fuera[31]:
0 1 2
4  0.274992  0.228913  1.352917
5  0,886429  ­2,001637  ­0,371843
6  1.669025  ­0.438570  ­0.539741

En  [32]:  df.dropna(umbral=2)

194  |  Capítulo  7:  Limpieza  y  preparación  de  datos
Machine Translated by Google

Fuera[32]:
0 1 2
2  0.092908 NaN  0,769023
3  1,246435  NaN  ­1,296221  4  0,274992  
0,228913  1,352917
5  0,886429  ­2,001637  ­0,371843
6  1.669025  ­0.438570  ­0.539741

Rellenar  los  datos  que  faltan  

En  lugar  de  filtrar  los  datos  que  faltan  (y  posiblemente  descartar  otros  datos  junto  con  ellos),  es  
posible  que  desee  rellenar  los  "agujeros"  de  varias  maneras.  Para  la  mayoría  de  los  propósitos,  el  
método  fillna  es  la  función  caballo  de  batalla  a  utilizar.  Llamar  a  fillna  con  una  constante  reemplaza  
los  valores  faltantes  con  ese  valor:

En  [33]:  df.fillna(0)
Fuera[33]:
0 1
2  0  ­0.204708  0.000000  0.000000
1  ­0.555730  0.000000  0.000000
2  0,092908  0,000000  0,769023
3  1,246435  0,000000  ­1,296221
4  0,274992  0,228913  1,352917  5  0,886429  
­2,001637  ­0,371843
6  1.669025  ­0.438570  ­0.539741

Al  llamar  a  fillna  con  un  dict,  puede  usar  un  valor  de  relleno  diferente  para  cada  columna:

En  [34]:  df.fillna({1:  0.5,  2:  0})
Fuera[34]:
0 1 2
0  ­0.204708  0.500000  0.000000
1  ­0.555730  0.500000  0.000000
2  0,092908  0,500000  0,769023
3  1,246435  0,500000  ­1,296221  4  0,274992  
0,228913  1,352917
5  0,886429  ­2,001637  ­0,371843
6  1.669025  ­0.438570  ­0.539741

fillna  devuelve  un  nuevo  objeto,  pero  puede  modificar  el  objeto  existente  en  el  lugar:

En  [35]:  _  =  df.fillna(0,  inplace=True)

En  [36]:  d.f.
Fuera[36]:
0 1 2
0  ­0.204708  0.000000  0.000000  1  ­0.555730  
0.000000  0.000000
2  0,092908  0,000000  0,769023
3  1,246435  0,000000  ­1,296221
4  0.274992  0.228913  1.352917
5  0,886429  ­2,001637  ­0,371843  6  1,669025  
­0,438570  ­0,539741

7.1  Manejo  de  datos  faltantes  |  195
Machine Translated by Google

Los  mismos  métodos  de  interpolación  disponibles  para  la  reindexación  se  pueden  utilizar  con  fillna:

En  [37]:  df  =  pd.DataFrame(np.random.randn(6,  3))

En  [38]:  df.iloc[2:,  1]  =  NA

En  [39]:  df.iloc[4:,  2]  =  NA

En  [40]:  d.f.
Fuera[40]:
0 1 2
0  0.476985  3.248944  ­1.021228
1  ­0.577087  0.124121  0.302614
2  0.523772 NaN  1.343810
3  ­0.713544  4   NaN  ­2,370232
­1.860761 NaN  NaN
5  ­1.265934 Yaya Yaya

En  [41]:  df.fillna(método='rellenar')
Fuera[41]:
0 1 2
0  0.476985  3.248944  ­1.021228
1  ­0.577087  0.124121  0.302614
2  0,523772  0,124121  1,343810
3  ­0.713544  0.124121  ­2.370232
4  ­1.860761  0.124121  ­2.370232
5  ­1.265934  0.124121  ­2.370232

En  [42]:  df.fillna(método='rellenar',  límite=2)
Fuera[42]:
0 1 2
0  0.476985  3.248944  ­1.021228
1  ­0.577087  0.124121  0.302614
2  0,523772  0,124121  1,343810
3  ­0.713544  0.124121  ­2.370232
4  ­1,860761  NaN  ­2,370232
5  ­1.265934 NaN  ­2,370232

Con  fillna  puedes  hacer  muchas  otras  cosas  con  un  poco  de  creatividad.  Por  ejemplo  tu
podría  pasar  el  valor  medio  o  mediano  de  una  Serie:

En  [43]:  datos  =  pd.Series([1.,  NA,  3.5,  NA,  7])

En  [44]:  data.fillna(data.mean())
Fuera[44]:
0  1.000000
1 3.833333
2 3.500000
3 3.833333
4 7.000000
tipo:  float64

Consulte  la  Tabla  7­2  para  obtener  una  referencia  sobre  fillna.

196  |  Capítulo  7:  Limpieza  y  preparación  de  datos
Machine Translated by Google

Tabla  7­2.  argumentos  de  la  función  fillna

Argumento  Descripción

valor Valor  escalar  u  objeto  similar  a  un  dictado  para  usar  para  completar  los  valores  que  faltan

método  Interpolación;  por  defecto  'rellenar'  si  se  llama  a  la  función  sin  otros  argumentos

eje Eje  para  rellenar;  eje  por  defecto  =0

inplace  Modificar  el  objeto  que  llama  sin  producir  una  copia

límite Para  llenado  hacia  adelante  y  hacia  atrás,  número  máximo  de  períodos  consecutivos  para  llenar

7.2  Transformación  de  datos
Hasta  ahora,  en  este  capítulo  nos  hemos  preocupado  por  reorganizar  los  datos.  Filtrado,  limpieza,
y  otras  transformaciones  son  otra  clase  de  operaciones  importantes.

Eliminación  de  duplicados

Se  pueden  encontrar  filas  duplicadas  en  un  DataFrame  por  varias  razones.  Aquí  hay  un
ejemplo:

En  [45]:  datos  =  pd.DataFrame({'k1':  ['uno',  'dos']  *  3  +  ['dos'],
.....: 'k2':  [1,  1,  2,  3,  3,  4,  4]})

En  [46]:  datos
Fuera[46]:
k1  k2
0  uno  1
1  dos  1
2  uno  2
3  dos  3
4  uno  3
5  dos  4
6  dos  4

El  método  DataFrame  duplicado  devuelve  una  serie  booleana  que  indica  si  cada
fila  es  un  duplicado  (se  ha  observado  en  una  fila  anterior)  o  no:

En  [47]:  datos.duplicados()
Fuera[47]:
0  falso
1 FALSO
2 FALSO
3 FALSO
4 FALSO
5 FALSO
Verdadero

6  dtipo:  bool

De  manera  relacionada,  drop_duplicates  devuelve  un  DataFrame  donde  se  encuentra  la  matriz  duplicada .
FALSO:

7.2  Transformación  de  datos  |  197
Machine Translated by Google

En  [48]:  data.drop_duplicates()
Salida[48]:  
k1  k2
0  uno  1  1  
dos  1
2  uno  2
3  dos  3
4  uno  3
5  dos  4

Ambos  métodos  por  defecto  consideran  todas  las  columnas;  como  alternativa,  puede  especificar  
cualquier  subconjunto  de  ellos  para  detectar  duplicados.  Supongamos  que  tuviéramos  una  columna  
adicional  de  valores  y  quisiéramos  filtrar  los  duplicados  solo  en  función  de  la  columna  'k1' :

En  [49]:  datos['v1']  =  rango(7)

En  [50]:  datos.drop_duplicates(['k1'])
Salida[50]:  
k1  k2  v1
0  uno  1  0
1  dos  1 1

duplicated  y  drop_duplicates  por  defecto  mantienen  la  primera  combinación  de  valores  observada.  
Pasar  keep='last'  devolverá  el  último:

En  [51]:  data.drop_duplicates(['k1',  'k2'],  keep='last')
Salida[51]:  
k1  k2  v1
0  uno  1  0  1  dos  1  
1
2  uno  2  2
3  dos  3  3
4  uno  3  4
6  dos  4  6

Transformación  de  datos  usando  una  función  o  mapeo  Para  muchos  

conjuntos  de  datos,  es  posible  que  desee  realizar  alguna  transformación  basada  en  los  valores  en  
una  matriz,  Serie  o  columna  en  un  DataFrame.  Considere  los  siguientes  datos  hipotéticos  recopilados  
sobre  varios  tipos  de  carne:

En  [52]:  datos  =  pd.DataFrame({'comida':  ['tocino',  'carne  de  cerdo  desmenuzada',  'tocino',
.....: 'Pastrami',  'corned  beef',  'Bacon',  'pastrami',  
.....: 'jamón  a  la  miel',  'nova  lox'],
.....: 'onzas':  [4,  3,  12,  6,  7,5,  8,  3,  5,  6]})

En  [53]:  datos
Fuera[53]:
comida  onzas  de  
tocino   4.0
0  1  tocino  de  puerco   3.0
desmenuzado  2 12.0

198  |  Capítulo  7:  Limpieza  y  preparación  de  datos
Machine Translated by Google

Pastrami  3   6.0
4  corned  beef 7.5
5 Tocino 8.0
6   pastrami   3.0
7 jamon  miel   5.0
8 nova  lox 6.0

Suponga  que  desea  agregar  una  columna  que  indique  el  tipo  de  animal  que  cada  alimento
vino  de.  Escribamos  un  mapeo  de  cada  tipo  de  carne  distinto  al  tipo  de
animal:

carne_a_animal  =  {
'tocino':  'cerdo',
'carne  de  cerdo  desmenuzada':  'cerdo',

'pastrami':  'vaca',
'corned  beef':  'vaca',
'jamón  de  miel':  'cerdo',
'nova  lox':  'salmón'
}

El  método  de  mapa  en  una  serie  acepta  una  función  o  un  objeto  similar  a  un  dictado  que  contiene  un  mapa.
ping,  pero  aquí  tenemos  un  pequeño  problema  en  que  algunas  de  las  carnes  están  en  mayúsculas  y
otros  no  lo  son.  Por  lo  tanto,  necesitamos  convertir  cada  valor  a  minúsculas  usando  str.lower
Método  de  la  serie:

En  [55]:  minúsculas  =  data['comida'].str.lower()

En  [56]:  minúsculas
Fuera[56]:
0 tocino
1 cerdo  desmenuzado
2 tocino
pastrami
3  4 carne  en  conserva
5 tocino
6 pastrami
7 jamón  de  miel
8  nova  lox
Nombre:  alimento,  dtype:  objeto

En  [57]:  datos['animal']  =  minúsculas.map(carne_a_animal)

En  [58]:  datos
Fuera[58]:
comida  onzas  animal
0 tocino  de  cerdo 4.0
1  puerco  desmenuzado 3.0
2 tocino  de  cerdo 12,0  
3 Pastrami  4   6,0 vaca
carne  en  conserva 7.5 vaca
5 Tocino 8.0 cerdo
6 pastrami 3.0 vaca

7.2  Transformación  de  datos  |  199
Machine Translated by Google

7 jamón  de   5,0   cerdo


8 miel  nova  lox 6,0  salmón

También  podríamos  haber  pasado  una  función  que  hiciera  todo  el  trabajo:

En  [59]:  data['food'].map(lambda  x:  meat_to_animal[x.lower()])
Fuera[59]:
0  cerdo

1   cerdo
2 cerdo
3 vaca
4 vaca
5 cerdo
6   vaca
7 cerdo
8 salmón
Nombre:  alimento,  dtype:  objeto

El  uso  del  mapa  es  una  forma  conveniente  de  realizar  transformaciones  por  elementos  y  otras
operaciones  relacionadas  con  la  limpieza  de  datos.

Sustitución  de  valores

Rellenar  los  datos  que  faltan  con  el  método  fillna  es  un  caso  especial  de  valor  más  general
reemplazo.  Como  ya  ha  visto,  el  mapa  se  puede  utilizar  para  modificar  un  subconjunto  de  valores  en
un  objeto  pero  replace  proporciona  una  forma  más  simple  y  flexible  de  hacerlo.  Vamos  a  con
Considere  esta  serie:

En  [60]:  datos  =  pd.Series([1.,  ­999.,  2.,  ­999.,  ­1000.,  3.])

En  [61]:  datos
Fuera[61]:
0 1.0
1 ­999.0
2 2.0
3  ­999.0
4  ­1000.0
5 3.0
tipo:  float64

Los  valores  ­999  pueden  ser  valores  centinela  para  datos  faltantes.  Para  reemplazar  estos  con  NA
valores  que  pandas  entiende,  podemos  usar  replace,  produciendo  una  nueva  serie  (a  menos  que
pasas  inplace=True):

En  [62]:  data.replace(­999,  np.nan)
Fuera[62]:
0 1.0
1 Yaya
2   2.0
3 Yaya
4  ­1000.0

200  |  Capítulo  7:  Limpieza  y  preparación  de  datos
Machine Translated by Google

5 3.0
tipo:  float64

Si  desea  reemplazar  varios  valores  a  la  vez,  pase  una  lista  y  luego  el
valor  sustitutivo:

En  [63]:  datos.reemplazar([­999,  ­1000],  np.nan)
Fuera[63]:
0  1.0
1 Yaya
2 2.0
Yaya
3  4 Yaya
5 3.0
tipo:  float64

Para  usar  un  reemplazo  diferente  para  cada  valor,  pase  una  lista  de  sustitutos:

En  [64]:  datos.reemplazar([­999,  ­1000],  [np.nan,  0])
Fuera[64]:
0  1.0
1   Yaya
2 2.0
3 Yaya
4 0.0
5 3.0
tipo:  float64

El  argumento  pasado  también  puede  ser  un  dictado:

En  [65]:  datos.reemplazar({­999:  np.nan,  ­1000:  0})
Fuera[65]:
0  1.0
1 Yaya
2 2.0
3 Yaya
4  0.0
5  3.0
tipo:  float64

El  método  data.replace  es  distinto  de  data.str.replace,
que  realiza  la  sustitución  de  cadenas  por  elementos.  Nos  fijamos  en  estos
métodos  de  cadena  en  Series  más  adelante  en  el  capítulo.

Cambio  de  nombre  de  índices  de  eje

Al  igual  que  los  valores  de  una  serie,  las  etiquetas  de  los  ejes  se  pueden  transformar  de  manera  similar  mediante  una  función  o  un  mapa.

ping  de  alguna  forma  para  producir  objetos  nuevos,  etiquetados  de  manera  diferente.  También  puedes  modificar
los  ejes  en  el  lugar  sin  crear  una  nueva  estructura  de  datos.  Aquí  hay  un  ejemplo  simple:

7.2  Transformación  de  datos  |  201
Machine Translated by Google

En  [66]:  datos  =  pd.DataFrame(np.arange(12).reshape((3,  4)),
.....: index=['Ohio',  'Colorado',  'Nueva  York'],
.....: columnas  =  ['uno',  'dos',  'tres',  'cuatro'])

Al  igual  que  una  serie,  los  índices  de  los  ejes  tienen  un  método  de  mapa :

En  [67]:  transformar  =  lambda  x:  x[:4].upper()

En  [68]:  data.index.map(transformar)
Salida[68]:  Índice(['OHIO',  'COLO',  'NUEVO  '],  dtype='objeto')

Puede  asignar  al  índice,  modificando  el  DataFrame  en  el  lugar:

En  [69]:  data.index  =  data.index.map(transformar)

En  [70]:  datos
Fuera[70]:
uno  dos  tres  CUATRO
OHIO 0 1 2 3
COLO 4 5 6 7
NUEVO 8 9 10 11

Si  desea  crear  una  versión  transformada  de  un  conjunto  de  datos  sin  modificar  el  original
nal,  un  método  útil  es  renombrar:

En  [71]:  data.rename(index=str.title,  column=str.upper)
Fuera[71]:
UNO  DOS  TRES  CUATRO
Ohio 0 1 2 3
Colo 4 5 6 7
Nuevo 8 9 10 11

En  particular,  el  cambio  de  nombre  se  puede  usar  junto  con  un  objeto  similar  a  un  dictado  que  proporciona  un  nuevo  valor.
ues  para  un  subconjunto  de  las  etiquetas  de  los  ejes:

En  [72]:  data.rename(index={'OHIO':  'INDIANA'},
.....: columnas  =  {'tres':  'cucú'})
Fuera[72]:
uno  dos  peekaboo  cuatro
INDIANA 0 1 2  3

COLO 4 5 6 7
NUEVO 8 9 10 11

renombrar  le  ahorra  la  tarea  de  copiar  el  DataFrame  manualmente  y  asignar
a  sus  atributos  de  índice  y  columnas .  Si  desea  modificar  un  conjunto  de  datos  en  el  lugar,
pase  en  el  lugar  =  Verdadero:

En  [73]:  data.rename(index={'OHIO':  'INDIANA'},  inplace=True)

En  [74]:  datos
Fuera[74]:
uno  dos  tres  CUATRO
INDIANA 0 1 2 3

202  |  Capítulo  7:  Limpieza  y  preparación  de  datos
Machine Translated by Google

COLO 4 5 6 7
NUEVO 8 9 10 11

Discretización  y  Binning
Los  datos  continuos  a  menudo  se  discretizan  o  se  separan  en  "contenedores"  para  su  análisis.
Suponga  que  tiene  datos  sobre  un  grupo  de  personas  en  un  estudio  y  desea  agrupar
en  cubos  de  edad  discretos:

En  [75]:  edades  =  [20,  22,  25,  27,  21,  23,  37,  31,  61,  45,  41,  32]

Dividámoslos  en  grupos  de  18  a  25,  26  a  35,  36  a  60  y  finalmente  61  y  más.  A
para  hacerlo,  tienes  que  usar  cut,  una  función  en  pandas:

En  [76]:  contenedores  =  [18,  25,  35,  60,  100]

En  [77]:  gatos  =  pd.cut(edades,  bins)

En  [78]:  gatos
Fuera[78]:
[(18,  25],  (18,  25],  (18,  25],  (25,  35],  (18,  25], ...,  (25,  35],  (60,  100],  (35,
60],  (35,  60],  (25,  35]]
Longitud:  12
Categorías  (4,  intervalo[int64]):  [(18,  25]  <  (25,  35]  <  (35,  60]  <  (60,  100)]

El  objeto  que  devuelve  pandas  es  un  objeto  categórico  especial .  La  salida  que  ves
describe  los  contenedores  calculados  por  pandas.cut.  Puedes  tratarlo  como  una  matriz  de  cadenas.
indicando  el  nombre  del  contenedor;  internamente  contiene  una  matriz  de  categorías  que  especifica  la  distribución
Nombres  de  categoría  de  tinte  junto  con  un  etiquetado  para  los  datos  de  edades  en  el  atributo  de  códigos :

En  [79]:  códigos.gatos
Salida[79]:  matriz([0,  0,  0,  1,  0,  0,  2,  1,  3,  2,  2,  1],  dtype=int8)

En  [80]:  gatos.categorias
Fuera[80]:
Índice  de  intervalo  ([(18,  25],  (25,  35],  (35,  60],  (60,  100]]
cerrado  =  'derecha',
dtype='intervalo[int64]')

En  [81]:  pd.value_counts(gatos)
Fuera[81]:
(18,  25]   5
(35,  60]   3
(25,  35]   3
(60,  100]   1
tipo:  int64

Tenga  en  cuenta  que  pd.value_counts(cats)  son  los  recuentos  de  contenedores  para  el  resultado  de  pandas.cut.

De  acuerdo  con  la  notación  matemática  para  intervalos,  un  paréntesis  significa  que  el  lado
está  abierto,  mientras  que  el  corchete  significa  que  está  cerrado  (inclusive).  Puedes  cambiar  cual
el  lado  se  cierra  pasando  right=False:

7.2  Transformación  de  datos  |  203
Machine Translated by Google

En  [82]:  pd.cut(edades,  [18,  26,  36,  61,  100],  derecha=Falso)
Fuera[82]:  
[[18,  26),  [18,  26),  [18,  26),  [26,  36),  [18,  26), ...,  [26,  36),  [61,  100 ),  [36,  61),  [36,  61 ),  [26,  36)]

Longitud:  
12  Categorías  (4,  intervalo[int64]):  [[18,  26)  <  [26,  36)  <  [36,  61)  <  [61,  100)]

También  puede  pasar  sus  propios  nombres  de  contenedores  pasando  una  lista  o  matriz  a  la  opción  de  etiquetas :

En  [83]:  group_names  =  ['Youth',  'YoungAdult',  'MiddleAged',  'Senior']

En  [84]:  pd.cut(edades,  bins,  etiquetas=nombres_de_grupo)
Fuera[84]:  
[Joven,  Joven,  Joven,  Adulto  joven,  Joven, ...,  Adulto  joven,  Mayor,  Edad  media,  Edad  media,  
Adulto  joven]
Longitud:  
12  Categorías  (4,  objeto):  [Youth  <  YoungAdult  <  MiddleAged  <  Senior]

Si  pasa  un  número  entero  de  contenedores  para  cortar  en  lugar  de  bordes  de  contenedores  explícitos,  calculará  
contenedores  de  igual  longitud  en  función  de  los  valores  mínimo  y  máximo  de  los  datos.
Considere  el  caso  de  algunos  datos  uniformemente  distribuidos  divididos  en  cuartos:

En  [85]:  datos  =  np.random.rand(20)

En  [86]:  pd.cut(data,  4,  precision=2)
Salida[86]:  
[(0.34,  0.55],  (0.34,  0.55],  (0.76,  0.97],  (0.76 ,  0.97],  (0.34,  0.55 ], ...,  (0.34 ,  0.55)],  (0.34,  0.55 ],  
(0,55,  0,76],  (0,34,  0,55],  (0,12,  0,34]]
Longitud:  
20  Categorías  (4,  intervalo[float64]):  [(0.12,  0.34]  <  (0.34,  0.55]  <  (0.55,  0.76]  <  (0.76,  0.97)]

La  opción  precision=2  limita  la  precisión  decimal  a  dos  dígitos.

Una  función  estrechamente  relacionada,  qcut,  agrupa  los  datos  en  función  de  cuantiles  de  muestra.  Dependiendo  
de  la  distribución  de  los  datos,  el  uso  de  corte  generalmente  no  dará  como  resultado  que  cada  contenedor  tenga  
la  misma  cantidad  de  puntos  de  datos.  Dado  que  qcut  usa  cuantiles  de  muestra  en  su  lugar,  por  definición  obtendrá  
contenedores  de  aproximadamente  el  mismo  tamaño:

En  [87]:  data  =  np.random.randn(1000)  #  Normalmente  distribuida

En  [88]:  cats  =  pd.qcut(data,  4)  #  Cortar  en  cuartiles

Entrada  [89]:  
gatos  
Salida[89]:  [(­0.0265,  0.62],  (0.62,  3.928],  (­0.68,  ­0.0265],  (0.62,  3.928],  (­0.0265,  0.62] , ...,  (­0,68,  
­0,0265],  (­0,68,  ­0,0265],  (­2,95,  ­0,68],  (0,62,  3,928],  (­0,68,  ­0,0265]]

Longitud:  1000  
Categorías  (4,  intervalo[float64]):  [(­2.95,  ­0.68]  <  (­0.68,  ­0.0265]  <  (­0.0265,  0.62]  <

(0.62,  3.928]]

204  |  Capítulo  7:  Limpieza  y  preparación  de  datos
Machine Translated by Google

En  [90]:  pd.value_counts(gatos)
Salida[90]:  
(0.62,  3.928]   250
(­0.0265,  0.62]   250  
(­0.68,  ­0.0265]  (­2.95,   250
­0.68]  dtype:  int64 250

Similar  a  cortar,  puede  pasar  sus  propios  cuantiles  (números  entre  0  y  1,  inclusive):

En  [91]:  pd.qcut(datos,  [0,  0.1,  0.5,  0.9,  1.])
Salida[91]:  
[(­0.0265,  1.286],  (­0.0265,  1.286],  (­1.187,  ­0.0265],  (­0.0265,  1.286],  (­0.026  5,  1.286], ...,  (­1.187 ,  ­0.0265],  
(­1.187,  ­0.0265],  (­2.95,  ­1.187],  (­0.0265,  1.286],  (­1.187,  ­0.0265]]

Longitud:  1000  
Categorías  (4,  intervalo[float64]):  [(­2.95,  ­1.187]  <  (­1.187,  ­0.0265]  <  (­0.026  5,  1.286]  <

(1.286,  3.928]]

Regresaremos  a  cut  y  qcut  más  adelante  en  el  capítulo  durante  nuestra  discusión  sobre  las  operaciones  
de  agregación  y  de  grupo,  ya  que  estas  funciones  de  discretización  son  especialmente  útiles  para  el  
análisis  de  cuantiles  y  de  grupos.

Detección  y  filtrado  de  valores  atípicos  El  

filtrado  o  la  transformación  de  valores  atípicos  es  en  gran  medida  una  cuestión  de  aplicar  operaciones  de  matriz.
Considere  un  DataFrame  con  algunos  datos  normalmente  distribuidos:

En  [92]:  datos  =  pd.DataFrame(np.random.randn(1000,  4))

En  [93]:  datos.describe()
Fuera[93]:
0 1 2
3  cuentas  1000.000000  1000.000000  1000.000000  1000.000000
significar 0.049091 0.026112 ­0.002544 ­0.051827
estándar 0.996947 1.007458 0.995232 0.998311
min ­3.645860 ­3.184377 ­3.745356 ­3.428254
25%   ­0.599807   ­0.612162   ­0.687373   ­0.747478  
50% 0.047101 ­0.013609 ­0.022158 ­0.088274
75% 0.756646 0.695298 0.699046 0.623331
máximo 2.653656 3.525865 2.735527 3.366626

Suponga  que  desea  encontrar  valores  en  una  de  las  columnas  que  excedan  3  en  valor  absoluto:

En  [94]:  columna  =  datos[2]

En  [95]:  col[np.abs(col)  >  3]
Salida[95]:  
41 ­3.399312
136  ­3.745356  
Nombre:  2,  tipo  de  d:  float64

7.2  Transformación  de  datos  |  205
Machine Translated by Google

Para  seleccionar  todas  las  filas  que  tengan  un  valor  superior  a  3  o  ­3,  puede  utilizar  cualquier  método  en  un  
DataFrame  booleano:

En  [96]:  datos[(np.abs(datos)  >  3).cualquiera(1)]
Fuera[96]:
0 1 2 3
41  0.457246  ­0.025907  ­3.399312  ­0.974657
60  1,951312  3,260383  0,963301  1,201206  136  0,508391  
­0,196713  ­3,745356  ­1,520113
235  ­0.242459  ­3.056990  1.918403  ­0.578828
258  0,682841  0,326045  0,425384  ­3,428254
322  1.179227  ­3.184377  1.369891  ­1.074833
544  ­3,548824  1,553205  ­2,186301  1,277104  635  ­0,578093  
0,193299  1,397822  3,366626
782  ­0.207434  3.525865  0.283070  0.544635
803  ­3,645860  0,255475  ­0,549574  ­1,907459

Los  valores  se  pueden  establecer  en  función  de  estos  criterios.  Aquí  hay  un  código  para  limitar  los  valores  fuera  del  
intervalo  –3  a  3:

En  [97]:  datos[np.abs(datos)  >  3]  =  np.sign(datos)  *  3

En  [98]:  datos.describe()
Fuera[98]:
0 1 2 3
cuenta  1000.000000  1000.000000  1000.000000  1000.000000  0.050286  0.025567  
significar ­0.051765 ­0.001399
estándar   0.992920 1.004214 0.991414 0.995761
mínimo ­3.000000 ­3.000000 ­3.000000 ­3.000000
25% ­0.599807 ­0.612162 ­0.687373 ­0.747478
50%   0.047101   ­0.013609   ­0.022158   ­0.088274  
75% 0.756646 0.695298 0.699046 0.623331
máximo 2.653656 3.000000 2.735527 3.000000

La  declaración  np.sign(data)  produce  valores  1  y  –1  en  función  de  si  los  valores  en  data  son  positivos  o  negativos:

En  [99]:  np.sign(datos).head()
Salida[99]:  
0 1 2 3
0  ­1,0  1,0  ­1,0  1,0
1  1,0  ­1,0  1,0  ­1,0
2  1,0  1,0  1,0  ­1,0  3  ­1,0  ­1,0  1,0  
­1,0
4  ­1,0  1,0  ­1,0  ­1,0

Permutación  y  muestreo  aleatorio  Permutar  (reordenar  

aleatoriamente)  una  serie  o  las  filas  en  un  marco  de  datos  es  fácil  de  hacer  usando  la  función  
numpy.random.permutation .  Llamar  a  la  permutación  con  la  longitud  del  eje  que  desea  permutar  produce  una  matriz  
de  números  enteros  que  indican  el  nuevo  orden:

206  |  Capítulo  7:  Limpieza  y  preparación  de  datos
Machine Translated by Google

En  [100]:  df  =  pd.DataFrame(np.arange(5  *  4).reshape((5,  4)))

En  [101]:  muestra  =  np.random.permutation(5)

En  [102]:  muestreador
Salida[102]:  matriz([3,  1,  4,  2,  0])

Esa  matriz  se  puede  usar  en  la  indexación  basada  en  iloc  o  en  la  función  de  toma  equivalente :

En  [103]:  d.f.
Salida[103]:  
0  1 2 3
0 0 1 2 3
1 4 5 6 7
2 8 9  10  11
3  12  13  14  15  4  16  17  
18  19

En  [104]:  df.take(muestreador)
Salida[104]:  
0  3  3  12  13  214  15

1 4 5 6 7
4  16  17  18  19
2 8 9  10  11
0 0 1 2 3

Para  seleccionar  un  subconjunto  aleatorio  sin  reemplazo,  puede  usar  el  método  de  muestra  en
Serie  y  marco  de  datos:

En  [105]:  df.muestra(n=3)
Salida[105]:  
0  1 2 3
3  12  13  14  15
4  16  17  18  19
2 8 9  10  11

Para  generar  una  muestra  con  reemplazo  (para  permitir  opciones  repetidas),  pase  replace=True  
a  la  muestra:

En  [106]:  opciones  =  pd.Series([5,  7,  ­1,  6,  4])

En  [107]:  sorteos  =  opciones.muestra  (n  =  10,  reemplazar  =  Verdadero)

En  [108]:  sorteos
Salida[108]:  
4 4
1 7
4   4
2 ­1
0 5
3 6
1 7

7.2  Transformación  de  datos  |  207
Machine Translated by Google

4 4
0 5
4 4
tipo:  int64

Indicador  de  computación/variables  ficticias  Otro  tipo  

de  transformación  para  aplicaciones  de  modelado  estadístico  o  aprendizaje  automático  es  convertir  
una  variable  categórica  en  una  matriz  "ficticia"  o  "indicadora".  Si  una  columna  en  un  DataFrame  
tiene  k  valores  distintos,  usted  derivaría  una  matriz  o  DataFrame  con  k  columnas  que  contienen  
todos  1  y  0.  pandas  tiene  una  función  get_dummies  para  hacer  esto,  aunque  diseñar  una  no  es  
difícil.  Volvamos  a  un  ejemplo  anterior  de  DataFrame:

En  [109]:  df  =  pd.DataFrame({'clave':  ['b',  'b',  'a',  'c',  'a',  'b'],  'datos1':  rango(6) })
.....:

En  [110]:  pd.get_dummies(df['clave'])
Salida[110]:  
abc
0  0  1  0  1  0  1  
0
2  1  0  0
3  0  0  1
4  1  0  0
5  0  1  0

En  algunos  casos,  es  posible  que  desee  agregar  un  prefijo  a  las  columnas  en  el  marco  de  datos  
del  indicador,  que  luego  se  puede  fusionar  con  los  otros  datos.  get_dummies  tiene  un  argumento  
de  prefijo  para  hacer  esto:

En  [111]:  maniquíes  =  pd.get_dummies(df['clave'],  prefijo='clave')

En  [112]:  df_with_dummy  =  df[['data1']].join(dummies)

En  [113]:  df_with_dummy
Salida[113]:  
dato1  clave_a  clave_b  clave_c  0  0
0   1
0  1 0  1 0 1
2 2 1 0 0
3 3 0 0 1
4 4 1 0 0
5 5 0 1 0

Si  una  fila  en  un  DataFrame  pertenece  a  varias  categorías,  las  cosas  son  un  poco  más  complicadas.  
Veamos  el  conjunto  de  datos  MovieLens  1M,  que  se  investiga  con  más  detalle  en  el  Capítulo  14:

208  |  Capítulo  7:  Limpieza  y  preparación  de  datos
Machine Translated by Google

En  [114]:  mnames  =  ['movie_id',  'title',  'genres']

En  [115]:  películas  =  pd.read_table('conjuntos  de  datos/lentes  de  película/películas.dat',  sep='::',
.....: encabezado=Ninguno,  nombres=mnombres)

En  [116]:  películas[:10]
Salida[116]:  
movie_id   título géneros
Toy  Story  (1995)  Animación|Infantil|Comedia
0  1 1  2 Jumanji  (1995)  Aventura|Infantil|Fantasía
2 3 Viejos  gruñones  (1995) Comedia|Romance
3 4 Esperando  para  exhalar  (1995) Comedia|Drama
4 5  Padre  de  la  novia  Parte  II  (1995)  6 Comedia
5   Calor  (1995)  7 Acción|Crimen|Suspense
6 sabina  (1995) Comedia|Romance
7 8 Tom  y  Huck  (1995) Aventura|Infantil
8 9 Muerte  súbita  (1995) Acción
9 10 Ojo  dorado  (1995) Acción|Aventura|Suspense

Agregar  variables  indicadoras  para  cada  género  requiere  un  poco  de  discusión.  Primero,  extraemos  
la  lista  de  géneros  únicos  en  el  conjunto  de  datos:
En  [117]:  todos  los  géneros  =  []

En  [118]:  para  x  en  películas.géneros:
.....: all_genres.extend(x.split('|'))

En  [119]:  géneros  =  pd.unique(all_genres)

Ahora  tenemos:

En  [120]:  géneros
Out[120]:  
array(['Animación',  "Infantil",  'Comedia',  'Aventura',  'Fantasía',
'Romance',  'Drama',  'Acción',  'Crimen',  'Suspense',  'Terror',
'Ciencia  ficción',  'Documental',  'Guerra',  'Musical',  'Misterio',  'Cine  negro',
'Occidental'],  dtype=objeto)

Una  forma  de  construir  el  DataFrame  del  indicador  es  comenzar  con  un  DataFrame  de  todos
ceros:

En  [121]:  zero_matrix  =  np.zeros((len(películas),  len(géneros)))

En  [122]:  dummies  =  pd.DataFrame(zero_matrix,  column=genres)

Ahora,  itere  a  través  de  cada  película  y  configure  las  entradas  en  cada  fila  de  dummies  en  1.  Para  
hacer  esto,  usamos  dummies.columns  para  calcular  los  índices  de  columna  para  cada  género:

7.2  Transformación  de  datos  |  209
Machine Translated by Google

En  [123]:  gen  =  películas.géneros[0]

En  [124]:  gen.split('|')
Out[124]:  ['Animación',  "Infantil",  'Comedia']

En  [125]:  dummies.columns.get_indexer(gen.split('|'))
Salida[125]:  matriz([0,  1,  2])

Luego,  podemos  usar .iloc  para  establecer  valores  basados  en  estos  índices:

En  [126]:  para  i,  gen  en  enumerate(movies.genres):
.....: índices  =  maniquíes.columnas.get_indexer(gen.split('|'))  maniquíes.iloc[i,  
.....: índices]  =  1
.....:

Entonces,  como  antes,  puedes  combinar  esto  con  películas:

En  [127]:  movies_windic  =  movies.join(dummies.add_prefix('Genre_'))

En  [128]:  películas_windic.iloc[0]
Salida[128]:  
movie_id   1
título Historia  del  juguete  (1995)
géneros Animación|Infantil|Comedia  1
Género_animación
Género_Infantil 1
Género_Comedia 1
género_aventura 0  

Género_Fantasía 0

Género_Romance 0
Género_Drama 0
...
Género_Crimen 0  

género_suspenso 0

género_terror 0
Género_Ciencia  ficción 0
Género_Documental 0
Género_Guerra 0  

Genero_Musical 0

género_misterio 0
Género_Cine­Noir 0
género_occidental 0
Nombre:  0,  Longitud:  21,  tipo  de  d:  objeto

Para  datos  mucho  más  grandes,  este  método  de  construcción  de  variables  
indicadoras  con  pertenencia  múltiple  no  es  especialmente  rápido.  Sería  
mejor  escribir  una  función  de  nivel  inferior  que  escriba  directamente  en  
una  matriz  NumPy  y  luego  envolver  el  resultado  en  un  DataFrame.

Una  receta  útil  para  aplicaciones  estadísticas  es  combinar  get_dummies  con  una  función  de  discre   tización  
como  cut:

210  |  Capítulo  7:  Limpieza  y  preparación  de  datos
Machine Translated by Google

En  [129]:  np.random.seed(12345)

En  [130]:  valores  =  np.random.rand(10)

In  [131]:  valores  
Out[131]:  
array([ 0.9296,  0.3164,  0.1839,  0.2046,  0.5677,  0.5955,  0.9645,  0.6532,  0.7489,  0.6536])

En  [132]:  contenedores  =  [0,  0.2,  0.4,  0.6,  0.8,  1]

En  [133]:  pd.get_dummies(pd.cut(valores,  bins))
Salida[133]:  
(0,0,  0,2]  (0,2,  0,4]  (0,4,  0,6]  (0,6,  0,8]  (0,8,  1,0]  1
0 0 0 0 0
1 0 1 0 0 0
2 1 0 0 0 0
3 0 1 0 0 0
4   0   0   0   0  
5 0 0 1  1 0 0
6 0 0 0 0 1
7 0 0 0 1 0
8 0 0 0 1 0
9 0 0 0 1 0

Configuramos  la  semilla  aleatoria  con  numpy.random.seed  para  que  el  ejemplo  sea  determinista.
Veremos  nuevamente  pandas.get_dummies  más  adelante  en  el  libro.

7.3  Manipulación  de  cadenas
Python  ha  sido  durante  mucho  tiempo  un  lenguaje  popular  de  manipulación  de  datos  sin  procesar,  en  parte  
debido  a  su  facilidad  de  uso  para  el  procesamiento  de  cadenas  y  texto.  La  mayoría  de  las  operaciones  de  
texto  se  simplifican  con  los  métodos  integrados  del  objeto  de  cadena.  Para  combinaciones  de  patrones  y  
manipulaciones  de  texto  más  complejas,  es  posible  que  se  necesiten  expresiones  regulares.  pandas  se  
suma  a  la  mezcla  al  permitirle  aplicar  cadenas  y  expresiones  regulares  de  manera  concisa  en  conjuntos  
completos  de  datos,  además  de  manejar  la  molestia  de  los  datos  faltantes.

Métodos  de  objetos  de  cadena  En  

muchas  aplicaciones  de  secuencias  de  comandos  y  secuencias  de  comandos,  los  métodos  de  cadena  
integrados  son  suficientes.  Como  ejemplo,  una  cadena  separada  por  comas  se  puede  dividir  en  partes  con  
split:

En  [134]:  val  =  'a,  b,  guido'

En  [135]:  val.split(',')
Fuera[135]:  ['a',  'b',  'guido']

split  a  menudo  se  combina  con  strip  para  recortar  espacios  en  blanco  (incluidos  los  saltos  de  línea):

7.3  Manipulación  de  cadenas  |  211
Machine Translated by Google

En  [136]:  piezas  =  [x.strip()  for  x  in  val.split(',')]

En  [137]:  piezas
Fuera[137]:  ['a',  'b',  'guido']

Estas  subcadenas  se  pueden  concatenar  junto  con  un  delimitador  de  dos  puntos  usando  la  suma:

En  [138]:  primero,  segundo,  tercero  =  piezas

En  [139]:  primero  +  '::'  +  segundo  +  '::'  +  tercero
Salida[139]:  'a::b::guido'

Pero  este  no  es  un  método  genérico  práctico.  Una  forma  más  rápida  y  más  pitónica  es  pasar  una  lista  o  tupla  
al  método  de  unión  en  la  cadena  '::':

En  [140]:  '::'.join(piezas)
Salida[140]:  'a::b::guido'

Otros  métodos  se  ocupan  de  localizar  subcadenas.  Usar  la  palabra  clave  in  de  Python  es  la  mejor  manera  de  
detectar  una  subcadena,  aunque  también  se  pueden  usar  index  y  find :

En  [141]:  'guido'  en  val
Salida[141]:  Verdadero

En  [142]:  val.index(',')
Salida[142]:  1

En  [143]:  val.find(':')
Salida[143]:  ­1

Tenga  en  cuenta  que  la  diferencia  entre  buscar  e  indexar  es  que  index  genera  una  excepción  si  no  se  
encuentra  la  cadena  (en  lugar  de  devolver  –1):

En  [144]:  val.index(':')
­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­  ­­­­­­­­­­­­­­­­­­­­­­­­­

ValueError   Rastreo  (última  llamada  más  reciente)
<ipython­input­144­280f8b2856ce>  en  <módulo>()  ­­­­>  1  
val.index(':')
ValueError:  subcadena  no  encontrada

De  manera  relacionada,  count  devuelve  el  número  de  ocurrencias  de  una  subcadena  en  particular:

En  [145]:  val.cuenta(',')
Salida[145]:  2

replace  sustituirá  las  ocurrencias  de  un  patrón  por  otro.  También  se  usa  comúnmente  para  eliminar  patrones,  
pasando  una  cadena  vacía:

En  [146]:  val.replace(',',  '::')
Salida[146]:  'a::b::guido'

En  [147]:  val.replace(',',  '')
Fuera[147]:  'ab  guido'

212  |  Capítulo  7:  Limpieza  y  preparación  de  datos
Machine Translated by Google

Consulte  la  Tabla  7­3  para  obtener  una  lista  de  algunos  de  los  métodos  de  cadena  de  Python.

Las  expresiones  regulares  también  se  pueden  usar  con  muchas  de  estas  operaciones,  como  verá.

Tabla  7­3.  Métodos  de  cadena  integrados  de  Python

Argumento Descripción  

contar Devuelve  el  número  de  ocurrencias  no  superpuestas  de  subcadena  en  la  cadena.

termina  con Devuelve  True  si  la  cadena  termina  con  sufijo.  

comienza  con  Devuelve  verdadero  si  la  cadena  comienza  con  un  prefijo.

unirse Use  una  cadena  como  delimitador  para  concatenar  una  secuencia  de  otras  cadenas.

índice Devuelve  la  posición  del  primer  carácter  en  la  subcadena  si  se  encuentra  en  la  cadena;  genera  ValueError  si  no  se  encuentra.

encontrar Devuelve  la  posición  del  primer  carácter  de  la  primera  aparición  de  subcadena  en  la  cadena;  como  índice,  pero  devuelve  –1  si  no  se  encuentra.

encontrar Devuelve  la  posición  del  primer  carácter  de  la  última  aparición  de  subcadena  en  la  cadena;  devuelve  –1  si  no  se  encuentra.

reemplazar Reemplace  las  ocurrencias  de  cadena  con  otra  cadena.

tira,  rstrip,   Recorte  los  espacios  en  blanco,  incluidos  los  saltos  de  línea;  equivalente  a  x.strip()  (y  rstrip,  lstrip,  respectivamente)  para  cada  elemento.

lstrip

dividir Divide  la  cadena  en  una  lista  de  subcadenas  utilizando  el  delimitador  pasado.

más  bajo Convierte  los  caracteres  del  alfabeto  a  minúsculas.

superior Convierte  los  caracteres  del  alfabeto  a  mayúsculas.

plegable Convierta  los  caracteres  a  minúsculas  y  convierta  cualquier  combinación  de  caracteres  variables  específica  de  la  región  a  una  forma  

comparable  común.

solo,  solo Justificación  a  la  izquierda  o  justificación  a  la  derecha,  respectivamente;  rellenar  el  lado  opuesto  de  la  cadena  con  espacios  (o  algún  

otro  carácter  de  relleno)  para  devolver  una  cadena  con  un  ancho  mínimo.

Expresiones  regulares  Las  

expresiones  regulares  proporcionan  una  forma  flexible  de  buscar  o  hacer  coincidir  patrones  de  cadenas  (a  menudo  más  complejos)  

en  el  texto.  Una  sola  expresión,  comúnmente  llamada  expresión  regular,  es  una  cadena  formada  de  acuerdo  con  el  lenguaje  de  

expresión  regular.  El  módulo  re  incorporado  de  Python  es  responsable  de  aplicar  expresiones  regulares  a  las  cadenas;  Voy  a  dar  

una  serie  de  ejemplos  de  su  uso  aquí.

El  arte  de  escribir  expresiones  regulares  podría  ser  un  capítulo  propio  
y,  por  lo  tanto,  está  fuera  del  alcance  del  libro.  Hay  muchos  tutoriales  
y  referencias  excelentes  disponibles  en  Internet  y  en  otros  libros.

Las  funciones  del  módulo  re  se  dividen  en  tres  categorías:  coincidencia  de  patrones,  sustitución  y  división.  Naturalmente,  todos  

estos  están  relacionados;  una  expresión  regular  describe  un  patrón  para  ubicar  en  el  texto,  que  luego  se  puede  usar  para  muchos  

propósitos.  Veamos  un  ejemplo  sencillo:

7.3  Manipulación  de  cadenas  |  213
Machine Translated by Google

Supongamos  que  queremos  dividir  una  cadena  con  un  número  variable  de  caracteres  de  espacio  en  blanco  
(tabulaciones,  espacios  y  saltos  de  línea).  La  expresión  regular  que  describe  uno  o  más  caracteres  de  espacio  en  
blanco  es  \s+:

En  [148]:  importar  re

En  [149]:  texto  =  "foo bar\t  baz  \tqux"

En  [150]:  re.split('\s+',  texto)
Salida[150]:  ['foo',  'bar',  'baz',  'qux']
Cuando  llama  a  re.split('\s+',  text),  primero  se  compila  la  expresión  regular  y  luego  se  llama  a  su  método  de  división  
en  el  texto  pasado.  Puede  compilar  la  expresión  regular  usted  mismo  con  re.compile,  formando  un  objeto  de  
expresión  regular  reutilizable:

En  [151]:  expresión  regular  =  re.compile('\s+')

En  [152]:  regex.split  (texto)
Salida[152]:  ['foo',  'bar',  'baz',  'qux']
Si,  en  cambio,  desea  obtener  una  lista  de  todos  los  patrones  que  coinciden  con  la  expresión  regular,  puede  usar  el  
método  findall :

En  [153]:  regex.findall(texto)
Salida[153]:  ['  ',  '\t  ',  '  \t']

Para  evitar  escapes  no  deseados  con  \  en  una  expresión  regular,  use  
literales  de  cadena  sin  procesar  como  r'C:\x'  en  lugar  del  equivalente  'C:\\x'.

Se  recomienda  encarecidamente  crear  un  objeto  regex  con  re.compile  si  tiene  la  intención  de  aplicar  la  misma  
expresión  a  muchas  cadenas;  hacerlo  ahorrará  ciclos  de  CPU.

match  y  search  están  estrechamente  relacionados  con  findall.  Mientras  que  findall  devuelve  todas  las  coincidencias  
en  una  cadena,  la  búsqueda  devuelve  solo  la  primera  coincidencia.  De  manera  más  rígida,  haga  coincidir  solo  las  
coincidencias  al  comienzo  de  la  cadena.  Como  un  ejemplo  menos  trivial,  consideremos  un  bloque  de  texto  y  una  
expresión  regular  capaz  de  identificar  la  mayoría  de  las  direcciones  de  correo  electrónico:

text  =  """Dave  [email protected]  
Steve  [email protected]  
Rob  [email protected]  
Ryan  [email protected]
"""
patrón  =  r'[A­Z0­9._%+­]+@[A­Z0­9.­]+\.[AZ]{2,4}'

#  re.IGNORECASE  hace  que  la  expresión  regular  no  distinga  entre  
mayúsculas  y  minúsculas  regex  =  re.compile(pattern,  flags=re.IGNORECASE)
El  uso  de  findall  en  el  texto  produce  una  lista  de  las  direcciones  de  correo  electrónico:

214  |  Capítulo  7:  Limpieza  y  preparación  de  datos
Machine Translated by Google

En  [155]:  regex.findall(texto)
Salida[155]:  
['[email protected]',  
'[email protected]',  
'[email protected]',  
'[email protected]']

la  búsqueda  devuelve  un  objeto  de  coincidencia  especial  para  la  primera  dirección  de  correo  electrónico  del  
texto.  Para  la  expresión  regular  anterior,  el  objeto  de  coincidencia  solo  puede  decirnos  la  posición  inicial  y  
final  del  patrón  en  la  cadena:

En  [156]:  m  =  expresión  regular.buscar(texto)

In  [157]:  m  
Out[157]:  <_sre.SRE_Match  object;  span=(5,  20),  partido='[email protected]'>

En  [158]:  texto[m.start():m.end()]
Salida[158]:  '[email protected]'

regex.match  devuelve  Ninguno,  ya  que  solo  coincidirá  si  el  patrón  ocurre  al  comienzo  de  la  cadena:

En  [159]:  imprimir  (regex.match  (texto))
Ninguno

De  manera  relacionada,  sub  devolverá  una  nueva  cadena  con  ocurrencias  del  patrón  reemplazadas  por  una  
nueva  cadena:

En  [160]:  print(regex.sub('ELIMINADO',  texto))
Dave  ELIMINADO
Steve  ELIMINADO
Rob  ELIMINADO
Ryan  ELIMINADO

Suponga  que  desea  encontrar  direcciones  de  correo  electrónico  y,  al  mismo  tiempo,  segmentar  cada  dirección  
en  sus  tres  componentes:  nombre  de  usuario,  nombre  de  dominio  y  sufijo  de  dominio.  Para  ello,  pon  entre  
paréntesis  las  partes  del  patrón  a  segmentar:

En  [161]:  patrón  =  r'([A­Z0­9._%+­]+)@([A­Z0­9.­]+)\.([AZ]{2,4})'

En  [162]:  expresión  regular  =  re.compile(patrón,  banderas=re.IGNORECASE)

Un  objeto  de  coincidencia  producido  por  esta  expresión  regular  modificada  devuelve  una  tupla  de  los  
componentes  del  patrón  con  su  método  de  grupos :

En  [163]:  m  =  regex.match('[email protected]')

En  [164]:  m.groups()
Out[164]:  ('wesm',  'bright',  'net')

findall  devuelve  una  lista  de  tuplas  cuando  el  patrón  tiene  grupos:

En  [165]:  regex.findall(texto)
Fuera[165]:

7.3  Manipulación  de  cadenas  |  215
Machine Translated by Google

[('dave',  'google',  'com'),  ('steve',  
'gmail',  'com'),  ('rob',  'gmail',  'com'),  
('ryan',  'yahoo  ',  'com')]

sub  también  tiene  acceso  a  grupos  en  cada  partido  usando  símbolos  especiales  como  \1  y  \2.  
El  símbolo  \1  corresponde  al  primer  grupo  emparejado,  \2  corresponde  al  segundo,  y  así  
sucesivamente:

En  [166]:  print(regex.sub(r'Nombre  de  usuario:  \1,  Dominio:  \2,  Sufijo:  \3',  texto))
Dave  Nombre  de  usuario:  dave,  Dominio:  google,  Sufijo:  com  Steve  
Nombre  de  usuario:  steve,  Dominio:  gmail,  Sufijo:  com  Rob  Nombre  
de  usuario:  rob,  Dominio:  gmail,  Sufijo:  com  Ryan  Nombre  de  
usuario:  ryan,  Dominio:  yahoo,  Sufijo:  com

Hay  mucho  más  en  las  expresiones  regulares  en  Python,  la  mayoría  de  las  cuales  están  fuera  del  alcance  
del  libro.  La  Tabla  7­4  proporciona  un  breve  resumen.

Tabla  7­4.  Métodos  de  expresiones  regulares

Argumento Descripción

encuentra  todos Devuelve  todos  los  patrones  coincidentes  que  no  se  superponen  en  una  cadena  como  una  lista

finditer  Como  findall,  pero  devuelve  un  iterador
fósforo Haga  coincidir  el  patrón  al  comienzo  de  la  cadena  y,  opcionalmente,  segmente  los  componentes  del  patrón  en  grupos;  si  el  patrón  

coincide,  devuelve  un  objeto  de  coincidencia  y,  de  lo  contrario,  Ninguno

buscar Escanee  la  cadena  en  busca  de  coincidencias  con  el  patrón;  devolver  un  objeto  de  coincidencia  si  es  así;  a  diferencia  de  la  coincidencia,  la  coincidencia  puede  

estar  en  cualquier  parte  de  la  cadena  en  lugar  de  solo  al  principio

dividir Rompe  la  cuerda  en  pedazos  en  cada  ocurrencia  del  patrón

sub,  subn  Reemplaza  todas  (sub)  o  las  primeras  n  apariciones  (subn)  del  patrón  en  la  cadena  con  la  expresión  de  reemplazo;  use  los  símbolos  \1,  \2, ...  

para  hacer  referencia  
a  los  elementos  del  grupo  de  coincidencias  en  la  cadena  de  reemplazo

Funciones  de  cadena  vectorizadas  en  pandas  La  limpieza  

de  un  conjunto  de  datos  desordenado  para  el  análisis  a  menudo  requiere  una  gran  cantidad  de  
manipulación  y  regularización  de  cadenas.  Para  complicar  las  cosas,  una  columna  que  contiene  cadenas  
a  veces  tendrá  datos  faltantes:

En  [167]:  datos  =  {'Dave':  '[email protected]',  'Steve':  '[email protected]',  'Rob':  '[email protected]',  
.....: 'Wes':  np.  yaya}

En  [168]:  datos  =  pd.Series(datos)

Entrada  [169]:  
datos  
Salida[169]:  
[email protected]  Dave  Rob  
Steve [email protected]  
[email protected]  NaN
Wes  dtype:  objeto

216  |  Capítulo  7:  Limpieza  y  preparación  de  datos
Machine Translated by Google

En  [170]:  datos.isnull()
Fuera[170]:  
Falso  Dave
Robar FALSO
Steve FALSO
Wes Verdadero

dtipo:  booleano

Puede  aplicar  cadenas  y  métodos  de  expresiones  regulares  (pasar  una  lambda  u  otra  función)  a  cada  
valor  usando  data.map,  pero  fallará  en  los  valores  NA  (nulos).  Para  hacer  frente  a  esto,  Series  tiene  
métodos  orientados  a  arreglos  para  operaciones  de  cadenas  que  omiten  valores  NA.  Se  accede  a  estos  a  
través  del  atributo  str  de  Series ;  por  ejemplo,  podríamos  verificar  si  cada  dirección  de  correo  electrónico  
tiene  'gmail'  con  str.contains:

En  [171]:  data.str.contains('gmail')
Fuera[171]:  
Dave  Falso  Rob  
Verdadero
Steve Verdadero

Wes Yaya

tipo:  objeto

También  se  pueden  usar  expresiones  regulares,  junto  con  cualquier  opción  re  como  IGNORECASE:

En  [172]:  patrón  Salida[172]:  
'([A­Z0­9._%+­]+)@([A­Z0­9.­]+)\\.([AZ]{2,  4})'

En  [173]:  data.str.findall(patrón,  banderas=re.IGNORECASE)
Fuera[173]:  
Dave  [(dave,  google,  com)]
robar  [(robar,  gmail,  com)]  [(steve,  gmail,  com)]
Steve
Wes Yaya

tipo:  objeto

Hay  un  par  de  formas  de  recuperar  elementos  vectorizados.  Utilice  str.get  o  indexe  en  el  atributo  str :

En  [174]:  coincidencias  =  data.str.match(patrón,  banderas=re.IGNORECASE)

En  [175]:  coincidencias
Fuera[175]:
dave Verdadero

Robar Verdadero

Steve Verdadero

Wes NaN  
dtype:  objeto

Para  acceder  a  los  elementos  de  las  listas  incrustadas,  podemos  pasar  un  índice  a  cualquiera  de  estas  
funciones:

En  [176]:  partidos.str.get(1)
Fuera[176]:

7.3  Manipulación  de  cadenas  |  217
Machine Translated by Google

dave Yaya
Robar Yaya
Steve  NaN
Wes Tipo  
de  NaN:  float64

En  [177]:  partidos.str[0]
Fuera[177]:  
Dave  NaN  Rob  
NaN
Steve  NaN
Wes Yaya

tipo:  float64

De  manera  similar,  puede  dividir  cadenas  usando  esta  sintaxis:

En  [178]:  datos.str[:5]
Fuera[178]:  
Dave  dave@  Rob  
rob@g  steve
Steve
Wes Yaya

tipo:  objeto

Consulte  la  Tabla  7­5  para  obtener  más  métodos  de  cadena  de  pandas.

Tabla  7­5.  Listado  parcial  de  métodos  de  cadenas  vectorizadas

Método Descripción  

gato Concatenar  cadenas  elemento­sabio  con  delimitador  opcional  

contiene Devolver  una  matriz  booleana  si  cada  cadena  contiene  patrón/

contar regex  Contar  ocurrencias  de  

extracto patrón  Usar  una  expresión  regular  con  grupos  para  extraer  una  o  más  cadenas  de  una  Serie  de  cadenas;  el  
resultado  será  un  DataFrame  con  una  columna  por  

termina  con grupo  Equivalente  a  x.endswith(patrón)  para  cada  elemento  

comienza  con Equivalente  a  x.startswith(patrón)  para  cada  elemento  Calcular  la  

encuentra  todos lista  de  todas  las  apariciones  de  patrón/regex  para  cada  cadena  

conseguir Índice  en  cada  elemento  ( recuperar  i­ésimo  elemento)

isalnum Equivalente  a  str.alnum  integrado  

isalfa Equivalente  a  str.isalpha  integrado  
isdecimal Equivalente  a  str.isdecimal  integrado

esdigito Equivalente  a  str.isdigit  incorporado
es  bajo Equivalente  a  str.islower  incorporado  

isnumeric Equivalente  a  str.isnumeric  incorporado  

essuperior Equivalente  a  str.isupper  incorporado  

unirse Unir  cadenas  en  cada  elemento  de  la  serie  con  el  separador  pasado  

Len Calcular  la  longitud  de  cada  cadena

mayúsculas  y  minúsculas  Convertir  mayúsculas  y  minúsculas;  equivalente  a  x.lower()  o  x.upper()  para  cada  elemento

218  |  Capítulo  7:  Limpieza  y  preparación  de  datos
Machine Translated by Google

Método Descripción  

fósforo Use  re.match  con  la  expresión  regular  pasada  en  cada  elemento,  devolviendo  los  grupos  coincidentes  como  una  lista  Agregue  

almohadilla
espacios  en  blanco  a  la  izquierda,  a  la  derecha  o  a  ambos  lados  

centro de  las  cadenas  Equivalente  a  pad(side='both')

repetir Valores  duplicados  (p.  ej.,  s.str.repeat(3)  es  equivalente  a  x  *  3  para  cada  cadena)

reemplazar Reemplazar  ocurrencias  de  patrón/regex  con  alguna  otra  cadena  Cortar  cada  

rebanada cadena  en  la  Serie  Dividir  cadenas  

dividir en  delimitador  o  expresión  regular  Recortar  espacios  en  

banda blanco  de  ambos  lados,  incluyendo  líneas  nuevas  Recortar  espacios  

rstrip  lstrip en  blanco  en  el  lado  derecho  Recortar  

espacios  en  blanco  en  el  lado  izquierdo

7.4  Conclusión
La  preparación  eficaz  de  datos  puede  mejorar  significativamente  la  productividad  al  
permitirle  dedicar  más  tiempo  a  analizar  datos  y  menos  tiempo  a  prepararlos  para  el  
análisis.  Hemos  explorado  una  serie  de  herramientas  en  este  capítulo,  pero  la  cobertura  
aquí  no  es  exhaustiva.  En  el  próximo  capítulo,  exploraremos  la  funcionalidad  de  unión  y  
agrupación  de  pandas.

7.4  Conclusión  |  219
Machine Translated by Google
Machine Translated by Google

CAPÍTULO  8

Gestión  de  datos:  unir,  combinar  
y  remodelar

En  muchas  aplicaciones,  los  datos  pueden  distribuirse  entre  varios  archivos  o  bases  de  datos  o  pueden  
organizarse  en  una  forma  que  no  es  fácil  de  analizar.  Este  capítulo  se  centra  en  las  herramientas  para  
ayudar  a  combinar,  unir  y  reorganizar  datos.

Primero,  presento  el  concepto  de  indexación  jerárquica  en  pandas,  que  se  usa  ampliamente  en  algunas  
de  estas  operaciones.  Luego  profundizo  en  las  manipulaciones  de  datos  particulares.
Puede  ver  varios  usos  aplicados  de  estas  herramientas  en  el  Capítulo  14.

8.1  Indexación  jerárquica
La  indexación  jerárquica  es  una  característica  importante  de  pandas  que  le  permite  tener  múltiples  (dos  
o  más)  niveles  de  índice  en  un  eje.  De  manera  un  tanto  abstracta,  proporciona  una  forma  de  trabajar  con  
datos  de  dimensiones  superiores  en  una  forma  de  dimensiones  inferiores.  Comencemos  con  un  ejemplo  
simple;  cree  una  serie  con  una  lista  de  listas  (o  matrices)  como  índice:

En  [9]:  datos  =  pd.Series(np.random.randn(9),
...: índice=[['a',  'a',  'a',  'b',  'b',  'c',  'c',  'd',  'd'],
...: [1,  2,  3,  1,  3,  1,  2,  2,  3]])

Entrada  [10]:  
datos  
Salida[10]:  a  1  ­0.204708
2 0.478943
3  ­0.519439  b  1  
­0.555730  3  1.965781

do  1 1.393406
2 0.092908
re  2 0.281746

221
Machine Translated by Google

3 0.769023
tipo:  float64

Lo  que  está  viendo  es  una  vista  embellecida  de  una  serie  con  un  índice  múltiple  como  índice.  Los  "espacios  en  
blanco"  en  la  visualización  del  índice  significan  "usar  la  etiqueta  directamente  arriba":

Entrada  [11]:  data.index  
Salida[11]:  
MultiIndex(niveles=[['a',  'b',  'c',  'd'],  [1,  2,  3]],
etiquetas=[[0,  0,  0,  1,  1,  2,  2,  3,  3],  [0,  1,  2,  0,  2,  0 ,  1,  1,  2]])

Con  un  objeto  indexado  jerárquicamente,  es  posible  la  llamada  indexación  parcial,  lo  que  le  permite  seleccionar  
subconjuntos  de  datos  de  manera  concisa:

En  [12]:  datos['b']
Salida[12]:  
1  ­0.555730  3  
1.965781  tipo  de  
d:  float64

En  [13]:  datos['b':'c']
Salida[13]:  
b  1  ­0.555730
3 1.965781
do  1 1.393406
2 0.092908
tipo:  float64

En  [14]:  data.loc[['b',  'd']]
Salida[14]:  
b  1  ­0.555730
1.965781  
3  días  2 0.281746
3 0.769023
tipo:  float64

La  selección  es  incluso  posible  desde  un  nivel  “interior”:

En  [15]:  data.loc[:,  2]
Fuera[15]:
a 0.478943
0.092908  
c  d  0.281746  
dtipo:  float64

La  indexación  jerárquica  juega  un  papel  importante  en  la  remodelación  de  datos  y  operaciones  basadas  en  
grupos,  como  la  formación  de  una  tabla  dinámica.  Por  ejemplo,  podría  reorganizar  los  datos  en  un  DataFrame  
usando  su  método  de  desapilar :

En  [16]:  datos.unstack()
Fuera[16]:
1 2 3
a  ­0.204708  0.478943  ­0.519439  b  
­0.555730 NaN  1.965781

222  |  Capítulo  8:  Gestión  de  datos:  unir,  combinar  y  remodelar
Machine Translated by Google

c  1.393406  0.092908  d Yaya
NaN  0,281746  0,769023

La  operación  inversa  de  desapilar  es  apilar:

En  [17]:  datos.unstack().stack()
Salida[17]:  
a  1  ­0.204708
2 0.478943
3  ­0.519439
b  1  ­0.555730  3  1.965781

do  1 1.393406
0.092908
2  días  2 0.281746
3  0.769023  dtipo:  
float64

apilar  y  desapilar  se  explorará  con  más  detalle  más  adelante  en  este  capítulo.

Con  un  DataFrame,  cualquiera  de  los  ejes  puede  tener  un  índice  jerárquico:

En  [18]:  marco  =  pd.DataFrame(np.arange(12).reshape((4,  3)),  index=[['a',  'a',  'b',  'b'],  [1 ,  
.....: 2,  1,  2]],  columnas=[['Ohio',  'Ohio',  'Colorado'],  ['Verde',  'Rojo',  
.....: 'Verde']])
.....:

En  [19]:  marco
Fuera[19]:
Ohio Colorado
Verde  rojo Verde
un  1  2   0   1 2
3 4 5
segundo  1 6 7 8
2 9  10 11

Los  niveles  jerárquicos  pueden  tener  nombres  (como  cadenas  o  cualquier  objeto  de  Python).  Si  es  así,  
aparecerán  en  la  salida  de  la  consola:

En  [20]:  frame.index.names  =  ['clave1',  'clave2']

En  [21]:  frame.columns.names  =  ['estado',  'color']

En  [22]:  cuadro
Out[22]:  
color   Ohio Colorado
de   Verde  rojo Verde
estado  tecla  1  tecla  2
a 1 0  1 2
2 3  4  6  7 5  

b 1 8
2 9  10 11

8.1  Indexación  jerárquica  |  223
Machine Translated by Google

Tenga  cuidado  de  distinguir  los  nombres  de  índice  'estado'  y  'color'  de  las  
etiquetas  de  las  filas.

Con  la  indexación  de  columnas  parciales,  puede  seleccionar  grupos  de  columnas  de  manera  similar:

En  [23]:  cuadro['Ohio']
Salida[23]:  
color Verde  rojo
clave1  clave2  
a 1 0 1
2 3 4
b 1 6 7
2 9  10

Un  MultiIndex  puede  crearse  por  sí  mismo  y  luego  reutilizarse;  las  columnas  de  la  anterior
DataFrame  con  nombres  de  nivel  podría  crearse  así:

MultiIndex.from_arrays([['Ohio',  'Ohio',  'Colorado'],  ['Verde',  'Rojo',  'Verde']],
nombres=['estado',  'color'])

Reordenación  y  clasificación  de  niveles  
A  veces  necesitará  reorganizar  el  orden  de  los  niveles  en  un  eje  o  clasificar  los  datos  por  
los  valores  en  un  nivel  específico.  El  nivel  de  intercambio  toma  dos  números  o  nombres  
de  nivel  y  devuelve  un  nuevo  objeto  con  los  niveles  intercambiados  (pero  los  datos  no  se  
modifican):

En  [24]:  frame.swaplevel('key1',  'key2')
Out[24]:  
color   Ohio Colorado
de   Verde  rojo Verde
estado  key2  key1
1 a 0  1  3  4 2
2 a 5
1 6  7 8
2 bb 9  10 11

sort_index,  por  otro  lado,  ordena  los  datos  usando  solo  los  valores  en  un  solo  nivel.
Al  intercambiar  niveles,  no  es  raro  usar  también  sort_index  para  que  el  resultado  se  
ordene  lexicográficamente  por  el  nivel  indicado:
En  [25]:  frame.sort_index(nivel=1)
Out[25]:  
color   Ohio Colorado
de   Verde  rojo Verde
estado  tecla  1  tecla  2
a 1 0  1 2
b 1 6  7  3  4 8  

a 2 5

224  |  Capítulo  8:  Gestión  de  datos:  unir,  combinar  y  remodelar
Machine Translated by Google

b 2 9  10 11

En  [26]:  frame.swaplevel(0,  1).sort_index(level=0)
Out[26]:  
color   Ohio Colorado
de   Verde  rojo Verde
estado  key2  key1
1 a 0  1 2
b 6  7  3  4 8  

2 a 5

b 9  10 11

El  rendimiento  de  la  selección  de  datos  es  mucho  mejor  en  objetos  
indexados  jerárquicamente  si  el  índice  se  ordena  lexicográficamente  
comenzando  con  el  nivel  más  externo,  es  decir,  el  resultado  de  
llamar  a  sort_index(level=0)  o  sort_index().

Estadísticas  de  resumen  por  nivel  

Muchas  estadísticas  descriptivas  y  de  resumen  en  DataFrame  y  Series  tienen  una  opción  de  
nivel  en  la  que  puede  especificar  el  nivel  por  el  que  desea  agregar  en  un  eje  en  particular.  
Considere  el  DataFrame  anterior;  podemos  agregar  por  nivel  en  las  filas  o  columnas  de  esta  
manera:

En  [27]:  frame.sum(level='key2')
Salida  [27]:  
estado  de  Ohio   Colorado
color  verde  rojo  key2 Verde

1 6  8  12   10  
2 14 16

En  [28]:  frame.sum(nivel='color',  eje=1)
Salida[28]:  
tecla   Verde  rojo
de  color  1  tecla  2
a 1 2 1
2 8 4
b 1 14 7
2 20  10

Debajo  del  capó,  esto  utiliza  la  maquinaria  groupby  de  los  pandas,  que  se  discutirá  con  más  
detalle  más  adelante  en  el  libro.

Indexación  con  las  columnas  de  un  DataFrame
No  es  inusual  querer  usar  una  o  más  columnas  de  un  DataFrame  como  índice  de  fila;  como  
alternativa,  es  posible  que  desee  mover  el  índice  de  la  fila  a  las  columnas  del  DataFrame.  
Aquí  hay  un  marco  de  datos  de  ejemplo:

8.1  Indexación  jerárquica  |  225
Machine Translated by Google

En  [29]:  cuadro  =  pd.DataFrame({'a':  rango(7),  'b':  rango(7,  0,  ­1),  'c':  ['uno',  'uno',  'uno  ',  
.....: 'dos',  'dos',  'dos',  'dos'],  'd':  [0,  1,  2,  0,  1,  2,  3]})
.....:
.....:

En  [30]:  cuadro
Salida[30]:  
a  b cd
0  0  7  uno  0  1  1  6  
uno  1
2  2  5  uno  2
3  3  4  dos  0
4  4  3  dos  1
5  5  2  dos  2  6  6  1  
dos  3

La  función  set_index  de  DataFrame  creará  un  nuevo  DataFrame  utilizando  una  o  más  de  sus  
columnas  como  índice:

En  [31]:  cuadro2  =  cuadro.set_index(['c',  'd'])

En  [32]:  fotograma2
Salida[32]:  
ab
C d
uno  0  0  7
1  1  6
2  2  5
dos  0  3  4  1  4  
3
2  5  2
3  6  1

De  forma  predeterminada,  las  columnas  se  eliminan  del  DataFrame,  aunque  puede  dejarlas  
en:

En  [33]:  frame.set_index(['c',  'd'],  soltar=Falso)
Salida[33]:  
ab cd
C d
uno  0  0  7  uno  0
1  1  6  uno  1  2  2  5  
uno  2
dos  0  3  4  dos  0
1  4  3  dos  1
2  5  2  dos  2
3  6  1  dos  3

reset_index,  por  otro  lado,  hace  lo  contrario  de  set_index;  los  niveles  de  índice  jerárquico  se  
mueven  a  las  columnas:

226  |  Capítulo  8:  Gestión  de  datos:  unir,  combinar  y  remodelar
Machine Translated by Google

En  [34]:  frame2.reset_index()
Salida[34]:  
cdab
0  uno  0  0  7  1  uno  1  
1  6
2  uno  2  2  5
3  dos  0  3  4
4  dos  1  4  3
5  dos  2  5  2  6  dos  3  
6  1

8.2  Combinación  y  fusión  de  conjuntos  de  datos
Los  datos  contenidos  en  los  objetos  pandas  se  pueden  combinar  de  varias  maneras:

•  pandas.merge  conecta  filas  en  DataFrames  en  función  de  una  o  más  claves.  Esto  resultará  familiar  para  los  
usuarios  de  SQL  u  otras  bases  de  datos  relacionales,  ya  que  implementa  operaciones  de  combinación  de  
bases  de  datos.

•  pandas.concat  concatena  o  “apila”  objetos  a  lo  largo  de  un  eje.  •  El  método  de  instancia  

combine_first  permite  empalmar  datos  superpuestos
para  completar  los  valores  que  faltan  en  un  objeto  con  valores  de  otro.

Abordaré  cada  uno  de  ellos  y  daré  una  serie  de  ejemplos.  Se  utilizarán  en  ejemplos  a  lo  largo  del  resto  del  libro.

Uniones  de  marcos  de  datos  de  estilo  de  base  de  datos

Las  operaciones  de  combinación  o  combinación  combinan  conjuntos  de  datos  al  vincular  filas  usando  una  o  más  claves.
Estas  operaciones  son  fundamentales  para  las  bases  de  datos  relacionales  (por  ejemplo,  basadas  en  SQL).  La  
función  de  combinación  en  pandas  es  el  principal  punto  de  entrada  para  usar  estos  algoritmos  en  sus  datos.

Comencemos  con  un  ejemplo  simple:

En  [35]:  df1  =  pd.DataFrame({'clave':  ['b',  'b',  'a',  'c',  'a',  'a',  'b'],
.....: 'datos1':  rango  (7)})

En  [36]:  df2  =  pd.DataFrame({'clave':  ['a',  'b',  'd'],  'datos2':  rango(3)})
.....:

En  [37]:  df1
Salida[37]:  
tecla  data1  b  
0
1 1b
0  
2 2 a
3 3 C
4 4 a
5 5 a

8.2  Combinación  y  fusión  de  conjuntos  de  datos  |  227
Machine Translated by Google

6 6 b

En  [38]:  df2
Salida[38]:  
tecla  data2  0
0 a
1 1
2 2 bd

Este  es  un  ejemplo  de  una  unión  de  muchos  a  uno;  los  datos  en  df1  tienen  múltiples  filas  etiquetadas  
como  a  y  b,  mientras  que  df2  tiene  solo  una  fila  para  cada  valor  en  la  columna  clave .  Llamando  a  
merge  con  estos  objetos  obtenemos:

En  [39]:  pd.merge(df1,  df2)
Salida[39]:  
datos1  clave  datos2  b  
0 0 1  b
1 1 1
2 6   b 1
3 2 a 0
4 4 a 0
5 5 a 0

Tenga  en  cuenta  que  no  especifiqué  en  qué  columna  unirse.  Si  no  se  especifica  esa  información,  merge  
utiliza  los  nombres  de  las  columnas  superpuestas  como  claves.  Sin  embargo,  es  una  buena  práctica  
especificar  explícitamente:

En  [40]:  pd.merge(df1,  df2,  on='clave')
Salida[40]:  
datos1  clave  datos2  b
0 0 1
1 1 1
2 6 bb 1
3 2 a 0
4 4 a 0
5 5 a 0

Si  los  nombres  de  las  columnas  son  diferentes  en  cada  objeto,  puede  especificarlos  por  separado:

En  [41]:  df3  =  pd.DataFrame({'lkey':  ['b',  'b',  'a',  'c',  'a',  'a',  'b'],
.....: 'datos1':  rango  (7)})

En  [42]:  df4  =  pd.DataFrame({'rkey':  ['a',  'b',  'd'],  'data2':  range(3)})
.....:

En  [43]:  pd.merge(df3,  df4,  left_on='lkey',  right_on='rkey')
Salida[43]:  
datos1  lkey  datos2  rkey  b  1  bb
0 0
1 1 1
2 6 bb  b 1
3 2 a 0 a

228  |  Capítulo  8:  Gestión  de  datos:  unir,  combinar  y  remodelar
Machine Translated by Google

4 4 a 0 a
5 5 a 0 a

Puede  notar  que  los  valores  'c'  y  'd'  y  los  datos  asociados  faltan  en  el  resultado.  Por  defecto,  merge  hace  una  unión  'interna' ;  las  

claves  en  el  resultado  son  la  intersección,  o  el  conjunto  común  que  se  encuentra  en  ambas  tablas.  Otras  opciones  posibles  son  

'izquierda',  'derecha'  y  'exterior'.  La  unión  externa  toma  la  unión  de  las  claves,  combinando  el  efecto  de  aplicar  las  uniones  

izquierda  y  derecha:

En  [44]:  pd.merge(df1,  df2,  how='outer')
Salida[44]:  
datos1  clave  datos2  0.0  
0 b  1.0
1 1,0   1,0  
2   segundo   1,0  
3 6,0  segundo  2,0  u0,0
n
4 4.0  un 0.0
5 5.0  un 0.0
6   3.0  c  NaN   NaN  
7 d 2.0

Consulte  la  Tabla  8­1  para  ver  un  resumen  de  las  opciones  de  cómo  hacerlo.

Tabla  8­1.  Diferentes  tipos  de  unión  con  argumento  how

Opción   Comportamiento

'interior'  Usar  solo  las  combinaciones  de  teclas  observadas  en  ambas  tablas

'izquierda' Usar  todas  las  combinaciones  de  teclas  que  se  encuentran  en  la  

tabla  de  la  izquierda  'derecha'  Usar  todas  las  combinaciones  de  teclas  que  se  encuentran  

en  la  tabla  de  la  derecha  'salida'  Usar  todas  las  combinaciones  de  teclas  observadas  en  ambas  tablas  juntas

Las  fusiones  de  muchos  a  muchos  tienen  un  comportamiento  bien  definido,  aunque  no  necesariamente  intuitivo.
Aquí  hay  un  ejemplo:

En  [45]:  df1  =  pd.DataFrame({'clave':  ['b',  'b',  'a',  'c',  'a',  'b'],  'datos1':  rango(6) })
.....:

En  [46]:  df2  =  pd.DataFrame({'clave':  ['a',  'b',  'a',  'b',  'd'],  'datos2':  rango(5)})
.....:

En  [47]:  df1
Salida[47]:  
tecla  data1  b
0 0
1   1   b
2 2 a
3 3 C
4 4 a
5 5 b

8.2  Combinación  y  fusión  de  conjuntos  de  datos  |  229
Machine Translated by Google

En  [48]:  df2
Salida[48]:  
tecla  data2  0  
1
0  1 abdominales

2 2 a
3 3
4 4 bd

En  [49]:  pd.merge(df1,  df2,  on='key',  how='left')
Salida[49]:  
datos1  clave  datos2  b
0 0 1.0
1   3.0  
2 0  1 1.0
3 1 bb  b 3.0
4 2 a 0.0
5 2 a 2.0
6   C NaN  
7 3  4 a 0.0
8 4 a 2.0
9 5 1.0
10 5 bb 3.0

Las  uniones  de  muchos  a  muchos  forman  el  producto  cartesiano  de  las  filas.  Dado  que  había  tres  filas  'b'  
en  el  DataFrame  izquierdo  y  dos  en  el  derecho,  hay  seis  filas  'b'  en  el  resultado.  El  método  de  combinación  
solo  afecta  a  los  distintos  valores  clave  que  aparecen  en  el  resultado:

En  [50]:  pd.merge(df1,  df2,  how='inner')
Salida[50]:  
datos1  clave  datos2  0  
b  0  bbb   1
0  1 b 3
2 1 1
3 1 3
4 5 1
5   5   b
6 2 a 3  0
7 2 a 2
8 4 a 0
9 4 a 2

Para  fusionar  con  varias  claves,  pase  una  lista  de  nombres  de  columna:

En  [51]:  izquierda  =  pd.DataFrame({'key1':  ['foo',  'foo',  'bar'],  'key2':  ['one',  'two',  'one'],  
.....: 'lval  ':  [1,  2,  3]})
.....:

En  [52]:  derecha  =  pd.DataFrame({'key1':  ['foo',  'foo',  'bar',  'bar'],  'key2':  ['one',  'one',  'one' ,  'dos'],  
.....: 'rval':  [4,  5,  6,  7]})
.....:

En  [53]:  pd.merge(left,  right,  on=['key1',  'key2'],  how='outer')

230  |  Capítulo  8:  Gestión  de  datos:  unir,  combinar  y  remodelar
Machine Translated by Google

Salida[53]:  
tecla1  tecla2  lval  rval  0  foo  
one  1.0  4.0
1  foo  uno  1,0  5,0  2  foo  dos  2,0  
NaN  3  bar  uno  3,0  6,0  4  bar  dos  
NaN  7,0

Para  determinar  qué  combinaciones  de  teclas  aparecerán  en  el  resultado  dependiendo  de  la  elección  del  método  de  
fusión,  piense  en  las  claves  múltiples  como  si  formaran  una  matriz  de  tuplas  que  se  utilizarán  como  una  sola  tecla  
de  unión  (aunque  en  realidad  no  se  implemente  de  esa  manera).

Cuando  une  columnas  sobre  columnas,  los  índices  de  los  
objetos  DataFrame  pasados  se  descartan.

Una  última  cuestión  a  tener  en  cuenta  en  las  operaciones  de  combinación  es  el  tratamiento  de  nombres  de  columnas  
superpuestos.  Si  bien  puede  abordar  la  superposición  manualmente  (consulte  la  sección  anterior  sobre  cómo  
cambiar  el  nombre  de  las  etiquetas  de  los  ejes),  la  combinación  tiene  una  opción  de  sufijos  para  especificar  cadenas  
para  agregar  a  los  nombres  superpuestos  en  los  objetos  DataFrame  izquierdo  y  derecho:

En  [54]:  pd.merge(izquierda,  derecha,  on='key1')
Salida[54]:  
clave1  clave2_x  lval  clave2_y  rval  0  foo  4  
1  foo uno 1 uno
uno 1 uno 5
2  foo  3   dos 2 uno 4
foo  4   dos 2 uno 5
bar  5   uno 3 uno 6
bar uno 3 dos 7

En  [55]:  pd.merge(izquierda,  derecha,  on='key1',  sufijos=('_left',  '_right'))
Salida[55]:  
tecla1  tecla2_izquierda  lval  tecla2_derecha  rval  0  
foo  4  1  foo uno 1 uno
uno 1 uno 5
2  foo  3   dos 2 uno 4
foo  4   dos 2 uno 5
bar  5   uno 3 uno 6
bar uno 3 dos 7

Consulte  la  Tabla  8­2  para  obtener  una  referencia  de  argumento  sobre  la  combinación.  Unirse  usando  el  índice  de  
fila  de  DataFrame  es  el  tema  de  la  siguiente  sección.

8.2  Combinación  y  fusión  de  conjuntos  de  datos  |  231
Machine Translated by Google

Tabla  8­2.  fusionar  argumentos  de  función

Argumento Descripción  

izquierda DataFrame  que  se  fusionará  en  el  lado  izquierdo.

DataFrame  que  se  fusionará  en  el  lado  derecho.
bien
cómo Uno  de  'interior',  'exterior',  'izquierda'  o  'derecha';  por  defecto  es  'interno'.
en Nombres  de  columna  para  unirse.  Debe  encontrarse  en  ambos  objetos  DataFrame.  Si  no  se  especifica  y  no  se  proporcionan  otras  claves  de  

combinación,  se  utilizará  la  intersección  de  los  nombres  de  las  columnas  a  la  izquierda  y  a  la  derecha  como  claves  de  combinación.

left_on   Columnas  en  el  marco  de  datos  izquierdo  para  usar  como  claves  de  combinación.

right_on   Análogo  a  left_on  para  el  DataFrame  izquierdo .

left_index  Usa  el  índice  de  fila  a  la  izquierda  como  su  clave  de  combinación  (o  claves,  

si  es  un  índice  múltiple).  right_index  Análogo  a  left_index.
clasificar Ordenar  datos  combinados  lexicográficamente  por  claves  de  combinación;  Verdadero  de  forma  predeterminada  (deshabilitar  para  obtener  un  mejor  rendimiento  

en  algunos  casos  en  grandes  conjuntos  de  datos).

sufijos Tupla  de  valores  de  cadena  para  agregar  a  los  nombres  de  columna  en  caso  de  superposición;  el  valor  predeterminado  es  ('_x',  '_y')  (por  ejemplo,  

si  'datos'  en  ambos  objetos  DataFrame,  aparecería  como  'datos_x'  y  'datos_y'  en  el  resultado).

Copiar Si  es  False,  evite  copiar  datos  en  la  estructura  de  datos  resultante  en  algunos  casos  excepcionales;  por  defecto  siempre  copia.

indicador  Añade  una  columna  especial  _merge  que  indica  el  origen  de  cada  fila;  los  valores  serán  'solo_izquierda',  'solo_derecha'  
o  'ambos'  según  el  origen  de  los  datos  unidos  en  cada  fila.

Fusionar  en  el  índice  
En  algunos  casos,  las  claves  de  fusión  en  un  DataFrame  se  encontrarán  en  su  índice.  En  
este  caso,  puede  pasar  left_index=True  o  right_index=True  (o  ambos)  para  indicar  que  el  
índice  debe  usarse  como  clave  de  combinación:

En  [56]:  izquierda1  =  pd.DataFrame({'clave':  ['a',  'b',  'a',  'a',  'b',  'c'],
.....: 'valor':  rango  (6)})

En  [57]:  right1  =  pd.DataFrame({'group_val':  [3.5,  7]},  index=['a',  'b'])

En  [58]:  izquierda1
Salida[58]:  
valor  clave  0
0 a
1 b 1
2 a 2
un  

3  4 segundo 3  4


5 C 5

Entrada  [59]:  right1  
Salida  [59]:  
group_val  3.5
a
b 7.0

232  |  Capítulo  8:  Gestión  de  datos:  unir,  combinar  y  remodelar
Machine Translated by Google

En  [60]:  pd.merge(left1,  right1,  left_on='key',  right_index=True)
Out[60]:  
valor  clave  group_val  0  3.5
0 a
2 a 2 3.5
3 a 3 3.5
1 b 1 7.0
4 b 4 7.0

Dado  que  el  método  de  fusión  predeterminado  es  intersectar  las  claves  de  combinación,  en  su  lugar  puede  formar  
la  unión  de  ellas  con  una  combinación  externa:

En  [61]:  pd.merge(left1,  right1,  left_on='key',  right_index=True,  how='outer')
Out[61]:  
valor  clave  group_val  0  3.5  
0   a 2  3.5
2 a
3 a 3 3.5
1 cama  y   1 7.0
4 desayuno 4 7.0
5 C 5 Yaya

Con  datos  indexados  jerárquicamente,  las  cosas  son  más  complicadas,  ya  que  unirse  en  el  índice  es  implícitamente  
una  combinación  de  varias  claves:

En  [62]:  lefth  =  pd.DataFrame({'key1':  ['Ohio',  'Ohio',  'Ohio',
.....: 'Nevada',  'Nevada'],  
.....: 'clave2':  [2000,  2001,  2002,  2001,  2002],  'datos':  
.....: np.arange(5.)})

En  [63]:  righth  =  pd.DataFrame(np.arange(12).reshape((6,  2)),  index=[['Nevada',  
.....: 'Nevada',  'Ohio',  'Ohio',
.....: 'Ohio',  'Ohio'],  [2001,  
.....: 2000,  2000,  2000,  2001,  2002]],  columnas=['evento1',  
.....: 'evento2'])

Entrada  [64]:  
izquierda  
Salida[64]:  datos  clave1  
clave2  0  0,0  Ohio  2000  1  1,0  
Ohio  2001  2  2,0  Ohio  2002  
3  3,0  Nevada  2001

4  4.0  Nevada  2002

En  [65]:  derecha
Fuera[65]:
evento1  evento2
Nevada  2001   0   1  

2000 2 3
Ohio  2000 4 5
2000 6 7

8.2  Combinación  y  fusión  de  conjuntos  de  datos  |  233
Machine Translated by Google

2001 8 9
2002 10 11

En  este  caso,  debe  indicar  varias  columnas  para  fusionarlas  como  una  lista  (tenga  en  cuenta  
el  manejo  de  valores  de  índice  duplicados  con  how='outer'):

En  [66]:  pd.merge(lefth,  righth,  left_on=['key1',  'key2'],  right_index=True)
Salida[66]:  
datos  clave1  clave2  evento1  evento2  0  0,0  Ohio  
2000  5  0  0,0  Ohio  2000  1  1,0  Ohio  2001  2  4
2,0  Ohio  2002
6 7
8 9
10 11
3  3.0  Nevada  2001 0 1

En  [67]:  pd.merge(izquierda,  derecha,  izquierda_activada=['tecla1',  'tecla2'],
.....: right_index=Verdadero,  cómo='exterior')
Salida[67]:  
datos  clave1  clave2  evento1  evento2  0  0,0  Ohio  
2000  5,0  0  0,0  Ohio  2000  1  1,0  Ohio  2001  
4.02  2,0  Ohio  
2002 6.0 7.0
8.0 9.0
10.0 11.0
3  3.0  Nevada  2001  4  4.0   0.0   1,0  
Nevada  2002  4  NaN  Nevada   NaN NaN
2000 2.0 3.0

También  es  posible  usar  los  índices  de  ambos  lados  de  la  fusión:

En  [68]:  left2  =  pd.DataFrame([[1.,  2.],  [3.,  4.],  [5.,  6.]],  index=['a',  'c',  'e  '],  columnas=['Ohio',  
.....: 'Nevada'])
.....:

En  [69]:  right2  =  pd.DataFrame([[7.,  8.],  [9.,  10.],  [11.,  12.],  [13,  14]],  index=['b',  'c',  'd',  'e'],  columnas=['Missouri',  
.....: 'Alabama'])
.....:

En  [70]:  izquierda2
Fuera[70]:
ohio  nevada
un  1.0 2.0
c3.0  _ 4.0
5.0  _ 6.0

En  [71]:  derecha2
Fuera[71]:
misuri  alabama
b 7.0 8.0
9,0   10,0  
cd 11,0 12,0
mi 13.0 14.0

En  [72]:  pd.merge(left2,  right2,  how='outer',  left_index=True,  right_index=True)

234  |  Capítulo  8:  Gestión  de  datos:  unir,  combinar  y  remodelar
Machine Translated by Google

Fuera[72]:
Ohio  Nevada  Misuri  Alabama
un  1.0 2.0 Yaya Yaya
b  NaN  c   NaN   7,0   8,0  
3,0  d  NaN 4.0 9,0 10,0
Yaya 11.0 12.0
5.0  _ 6.0 13.0 14.0

DataFrame  tiene  una  instancia  de  combinación  conveniente  para  fusionar  por  índice.  También  se  puede  
usar  para  combinar  muchos  objetos  DataFrame  que  tienen  índices  iguales  o  similares  pero  columnas  que  
no  se  superponen.  En  el  ejemplo  anterior,  podríamos  haber  escrito:

En  [73]:  left2.join(right2,  how='outer')
Fuera[73]:
Ohio  Nevada  Misuri  Alabama
un  1.0 2.0 Yaya Yaya
b  NaN  c   NaN   7,0   8,0  
3,0  d  NaN 4.0 9,0 10,0
Yaya 11.0 12.0
5.0  _ 6.0 13.0 14.0

En  parte  por  motivos  heredados  (es  decir,  versiones  mucho  más  antiguas  de  pandas),  el  método  de  
combinación  de  DataFrame  realiza  una  combinación  izquierda  en  las  teclas  de  combinación,  preservando  
exactamente  el  índice  de  fila  del  marco  izquierdo.  También  admite  unir  el  índice  del  DataFrame  pasado  en  
una  de  las  columnas  del  DataFrame  que  llama:

En  [74]:  left1.join(right1,  on='key')
Salida[74]:  
valor  clave  group_val  3.5
0 a 0
1 b 1 7.0
2 a 2 3.5
un   3.5  
3  4 segundo 3  4 7.0
5 C 5 Yaya

Por  último,  para  fusiones  simples  de  índice  sobre  índice,  puede  pasar  una  lista  de  DataFrames  para  unirse  
como  alternativa  al  uso  de  la  función  concat  más  general  que  se  describe  a  continuación.
sección:

En  [75]:  otro  =  pd.DataFrame([[7.,  8.],  [9.,  10.],  [11.,  12.],  [16.,  17.]],  index=['a  ',  'c',  'e',  'f'],  columnas=['Nueva  York',  'Oregón'])
.....:
.....:

Entrada  [76]:  otra  
Salida[76]:  
Nueva  York  Oregón  7,0  
a 8,0  9,0  10,0  11,0  
C 12,0
mi

F 16.0 17.0

8.2  Combinación  y  fusión  de  conjuntos  de  datos  |  235
Machine Translated by Google

En  [77]:  left2.join([right2,  otro])
Fuera[77]:  
Ohio  Nevada  Misuri  Alabama  Nueva  York  Oregón  a  1,0  2,0  8,0  c  3,0  4,0  10,0
NaN   7,0  
9.0 NaN  10.0 9,0
5.0  _ 6.0 13.0 14.0 11.0 12.0

En  [78]:  left2.join([right2,  another],  how='outer')
Fuera[78]:  
Ohio  Nevada  Misuri  Alabama  Nueva  York  Oregón  8.0
a  1,0  b  NaN 2.0 Yaya Yaya 7.0
Yaya 7.0 8.0 Yaya Yaya
c3.0  _ 4.0 9.0 10.0 9.0 10.0
d  NaN  e   NaN   11,0   12,0  
5,0  f  NaN 6.0 13,0 14,0 NaN  11.0 NaN  12.0
Yaya Yaya Yaya 16.0 17.0

Concatenación  a  lo  largo  de  un  eje  Otro  

tipo  de  operación  de  combinación  de  datos  se  conoce  indistintamente  como  concatenación,  enlace  o  
apilamiento.  La  función  de  concatenación  de  NumPy  puede  hacer  esto  con  matrices  NumPy:

En  [79]:  arr  =  np.arange(12).reshape((3,  4))

En  [80]:  arr
Salida[80]:  
matriz([[ 0,  1,  2,  3],  [ 4,  5,  6,  7],  [ 8,  9,  
10,  11]])

En  [81]:  np.concatenate([arr,  arr],  eje=1)
Salida[81]:  
matriz([[ 0,  1,  2,  3,  0,  1,  2,  3],  [ 4,  5,  6,  7,  4,  5,  6 ,  7],  [ 8,  9,  10 ,  
11,  8,  9,  10,  11]])

En  el  contexto  de  objetos  pandas  como  Series  y  DataFrame,  tener  ejes  etiquetados  le  permite  generalizar  
aún  más  la  concatenación  de  matrices.  En  particular,  tiene  una  serie  de  cosas  adicionales  en  las  que  
pensar:

•  Si  los  objetos  están  indexados  de  manera  diferente  en  los  otros  ejes,  ¿debemos  combinar  los  distintos  
elementos  en  estos  ejes  o  usar  solo  los  valores  compartidos  (la  intersección)?  •  ¿Los  fragmentos  

concatenados  de  datos  deben  ser  identificables  en  el  resultado?
¿objeto?

•  ¿El  “eje  de  concatenación”  contiene  datos  que  deben  conservarse?  En  muchos  casos,  es  mejor  
descartar  las  etiquetas  de  enteros  predeterminadas  en  un  DataFrame  durante  la  concatenación.

236  |  Capítulo  8:  Gestión  de  datos:  unir,  combinar  y  remodelar
Machine Translated by Google

La  función  concat  en  pandas  proporciona  una  forma  consistente  de  abordar  cada  una  de  estas  
preocupaciones.  Voy  a  dar  una  serie  de  ejemplos  para  ilustrar  cómo  funciona.  Supongamos  que  
tenemos  tres  series  sin  superposición  de  índice:

En  [82]:  s1  =  pd.Series([0,  1],  index=['a',  'b'])

En  [83]:  s2  =  pd.Series([2,  3,  4],  index=['c',  'd',  'e'])

En  [84]:  s3  =  pd.Series([5,  6],  index=['f',  'g'])

Llamar  a  concat  con  estos  objetos  en  una  lista  une  los  valores  y  los  índices:

En  [85]:  pd.concat([s1,  s2,  s3])
Salida[85]:  
a 0
b 1
C 2
d
mi 3  4

F 5
6
tipo  g:  int64

Por  defecto,  concat  funciona  a  lo  largo  del  eje  =  0,  produciendo  otra  Serie.  Si  pasa  axis=1,  el  
resultado  será  un  DataFrame  (axis=1  son  las  columnas):

En  [86]:  pd.concat([s1,  s2,  s3],  eje=1)
Salida[86]:  
0 1 2
a  0,0  NaN  NaN  b  1,0  
NaN  NaN  c  NaN  2,0  
NaN  d  NaN  3,0  NaN

e  NaN  4,0  NaN
f  NaN  NaN  5,0  g  NaN  
NaN  6,0

En  este  caso,  no  hay  superposición  en  el  otro  eje,  que  como  puede  ver  es  la  unión  ordenada  (la  
unión  'externa' )  de  los  índices.  En  su  lugar,  puede  cruzarlos  pasando  join='inner':

En  [87]:  s4  =  pd.concat([s1,  s3])

En  [88]:  s4
Salida[88]:  
a 0
1
bf 5
6
tipo  g:  int64

En  [89]:  pd.concat([s1,  s4],  eje=1)
Fuera[89]:

8.2  Combinación  y  fusión  de  conjuntos  de  datos  |  237
Machine Translated by Google

0  1
a  0.0  0  b  1.0  
1
f  NaN  5  g  
NaN  6

En  [90]:  pd.concat([s1,  s4],  axis=1,  join='inner')
Salida[90]:  
0  1  a  
0  0  b  1  1

En  este  último  ejemplo,  las  etiquetas  'f'  y  'g'  desaparecieron  debido  a  la  opción  join='inner' .

Incluso  puede  especificar  los  ejes  que  se  utilizarán  en  los  otros  ejes  con  join_axes:

En  [91]:  pd.concat([s1,  s4],  eje=1,  join_axes=[['a',  'c',  'b',  'e']])
Salida[91]:  
1
0  a  0.0  0.0
c  NaN  NaN  b  
1,0  1,0
e  NaN  NaN

Un  problema  potencial  es  que  las  piezas  concatenadas  no  son  identificables  en  el  resultado.  Suponga  que,  
en  cambio,  desea  crear  un  índice  jerárquico  en  el  eje  de  concatenación.  Para  hacer  esto,  use  el  argumento  
de  las  claves :

En  [92]:  resultado  =  pd.concat([s1,  s1,  s3],  claves=['uno',  'dos',  'tres'])

En  [93]:  resultado
Fuera[93]:
uno a 0
b 1
dos a 0
b 1
tres  f
gramo

5  6  dtipo:  int64

En  [94]:  resultado.unstack()
Fuera[94]:
a b F
uno g  0,0  1,0  NaN  NaN
dos  0,0  1,0  NaN  NaN  tres  NaN  
NaN  5,0  6,0

En  el  caso  de  combinar  Series  a  lo  largo  del  eje  =  1,  las  claves  se  convierten  en  los  encabezados  de  
columna  de  DataFrame:

En  [95]:  pd.concat([s1,  s2,  s3],  eje=1,  teclas=['uno',  'dos',  'tres'])
Fuera[95]:

238  |  Capítulo  8:  Gestión  de  datos:  unir,  combinar  y  remodelar
Machine Translated by Google

Uno,  dos,  tres
a  0,0  NaN  b  1,0   Yaya
NaN Yaya
c  NaN  2,0  d   Yaya
NaN  3,0 Yaya
e  NaN  4.0  f  NaN   Yaya
NaN 5.0
g  NaN  NaN 6.0

La  misma  lógica  se  extiende  a  los  objetos  DataFrame:

En  [96]:  df1  =  pd.DataFrame(np.arange(6).reshape(3,  2),  index=['a',  'b',  'c'],  column=['one',  'two  '])
.....:

En  [97]:  df2  =  pd.DataFrame(5  +  np.arange(4).reshape(2,  2),  index=['a',  'c'],
.....: columnas=['tres',  'cuatro'])

En  [98]:  df1
Fuera[98]:
uno  dos
a 0 1
b 2 3
C 4 5

En  [99]:  df2
Salida[99]:  
tres  cuatro
a 5 6
C 7 8

En  [100]:  pd.concat([df1,  df2],  eje=1,  teclas=['nivel1',  'nivel2'])
Salida[100]:  
nivel1 nivel  2
uno  dos  tres  cuatro  5.0  6.0
a 0 1
b 2 3 NaN  NaN
C 4 5 7,0  8,0

Si  pasa  un  dictado  de  objetos  en  lugar  de  una  lista,  las  claves  del  dictado  se  utilizarán  para  la  opción  
de  claves :

En  [101]:  pd.concat({'nivel1':  df1,  'nivel2':  df2},  eje=1)
Salida[101]:  
nivel1  nivel2  uno  dos  tres  
cuatro
a 0 1 5.0  6.0
b 2 3 NaN  NaN
C 4 5 7,0  8,0

Hay  argumentos  adicionales  que  gobiernan  cómo  se  crea  el  índice  jerárquico  (vea  la  Tabla  8­3).  Por  
ejemplo,  podemos  nombrar  los  niveles  de  eje  creados  con  el  argumento  de  nombres :

8.2  Combinación  y  fusión  de  conjuntos  de  datos  |  239
Machine Translated by Google

En  [102]:  pd.concat([df1,  df2],  eje=1,  teclas=['nivel1',  'nivel2'],  nombres=['superior',  'inferior'])
.....:
Salida[102]:  
superior  nivel1  nivel2  inferior  uno  
dos  tres  cuatro
a 0 1 5.0  6.0
b 2 3 NaN  NaN
C 4 5 7,0  8,0

Una  última  consideración  se  refiere  a  los  marcos  de  datos  en  los  que  el  índice  de  fila  no  contiene

Cualquier  dato  relevante:

En  [103]:  df1  =  pd.DataFrame(np.random.randn(3,  4),  columnas=['a',  'b',  'c',  'd'])

En  [104]:  df2  =  pd.DataFrame(np.random.randn(2,  3),  columnas=['b',  'd',  'a'])

En  [105]:  df1
Fuera[105]:
a b C d
0  1,246435  1,007189  ­1,296221  0,274992
1  0.228913  1.352917  0.886429  ­2.001637
2  ­0.371843  1.669025  ­0.438570  ­0.539741

En  [106]:  df2
Fuera[106]:
b d a
0  0,476985  3,248944  ­1,021228  1  ­0,577087  
0,124121  0,302614

En  este  caso,  puede  pasar  ignore_index=True:

En  [107]:  pd.concat([df1,  df2],  ignore_index=True)
Fuera[107]:
­1,296221  0,274992
bda  c  0  1,246435  1,007189  

1  0.228913  1.352917  0.886429  ­2.001637
2  ­0.371843  1.669025  ­0.438570  ­0.539741
3  ­1.021228  0.476985 NaN  3,248944
4  0,302614  ­0,577087 NaN  0,124121

Tabla  8­3.  argumentos  de  la  función  concat

objetos  de   Descripción

argumento Lista  o  dictado  de  objetos  pandas  para  ser  concatenados;  este  es  el  único  argumento  requerido

eje Eje  para  concatenar  a  lo  largo;  el  valor  predeterminado  es  0  (a  lo  largo  de  las  filas)

unirse Ya  sea  'interior'  o  'exterior' ('exterior'  por  defecto);  ya  sea  para  la  intersección  (interior)  o  la  unión  
(exterior)  de  índices  a  lo  largo  de  los  otros  ejes

unir_ejes Índices  específicos  para  usar  con  los  otros  n–1  ejes  en  lugar  de  realizar  la  lógica  de  unión/intersección

llaves Valores  para  asociar  con  objetos  que  se  concatenan,  formando  un  índice  jerárquico  a  lo  largo  del  eje  de  

concatenación;  puede  ser  una  lista  o  una  matriz  de  valores  arbitrarios,  una  matriz  de  tuplas  o  una  lista  de  

matrices  (si  las  matrices  de  varios  niveles  se  pasan  en  niveles)

240  |  Capítulo  8:  Gestión  de  datos:  unir,  combinar  y  remodelar
Machine Translated by Google

Argumento Descripción

niveles Índices  específicos  para  usar  como  nivel  o  niveles  de  índice  jerárquico  si  se  pasan  claves

nombres Nombres  para  los  niveles  jerárquicos  creados  si  las  claves  y /o  los  niveles  

pasaron  por  defecto  (Falso)  permite  duplicados

ignorar_índice No  conserve  los  índices  a  lo  largo  del  eje  de  concatenación,  sino  que  produzca  un  
nuevo  índice  de  rango  (longitud  total)

Combinación  de  datos  con  superposición  

Hay  otra  situación  de  combinación  de  datos  que  no  se  puede  expresar  como  una  operación  de  
combinación  o  concatenación.  Puede  tener  dos  conjuntos  de  datos  cuyos  índices  se  superpongan  
total  o  parcialmente.  Como  ejemplo  motivador,  considere  la  función  where  de  NumPy ,  que  realiza  
el  equivalente  orientado  a  matrices  de  una  expresión  if­else:

En  [108]:  a  =  pd.Series([np.nan,  2.5,  np.nan,  3.5,  4.5,  np.nan],
.....: índice=['f',  'e',  'd',  'c',  'b',  'a'])

En  [109]:  b  =  pd.Series(np.arange(len(a),  dtype=np.float64),  index=['f',  'e',  'd',  'c',  'b',  
.....: 'a'])

En  [110]:  b[­1]  =  np.nan

Entrada  [111]:  a  
Salida  [111]:  
f  NaN  2.5
mi

d Yaya
C 3.5
b 4.5

NaN  un  tipo  de  d:  float64

Entrada  [112]:  
b  Salida  
[112]:  f  0.0  
mi 1.0
d 2.0
C 3.0
b 4.0

NaN  un  tipo  de  d:  float64

En  [113]:  np.where(pd.isnull(a),  b,  a)
Salida[113]:  matriz([ 0. ,  3.5,  4.5,  nan]) ,  2.5,  2.

Series  tiene  un  método  combine_first ,  que  realiza  el  equivalente  de  esta  operación  junto  con  la  
lógica  de  alineación  de  datos  habitual  de  pandas:

8.2  Combinación  y  fusión  de  conjuntos  de  datos  |  241
Machine Translated by Google

En  [114]:  b[:­2].combine_first(a[2:])
Salida[114]:  
a NaN
b 4.5  
C 3.0
d 2.0
mi 1.0
F 0.0
tipo:  float64

Con  DataFrames,  combine_first  hace  lo  mismo  columna  por  columna,  por  lo  que  puede  
considerarlo  como  "parchar"  los  datos  que  faltan  en  el  objeto  que  llama  con  los  datos  del  objeto  
que  pasa:

En  [115]:  df1  =  pd.DataFrame({'a':  [1.,  np.nan,  5.,  np.nan],  'b':  [np.nan,  2.,  np.nan,  6. ],  'c':  
.....: rango  (2,  18,  4)})
.....:

En  [116]:  df2  =  pd.DataFrame({'a':  [5.,  4.,  np.nan,  3.,  7.],  'b':  [np.nan,  3.,  4.,  6 .,  8.]})
.....:

En  [117]:  df1
Fuera[117]:
a antes  de  Cristo

0  1,0  NaN  2
1  NaN  2,0  6
2  5,0  NaN  10  3  NaN  
6,0  14

En  [118]:  df2
Fuera[118]:

b  a  0  5.0  NaN
1  4,0  3,0
2  NaN  4,0
3  3,0  6,0
4  7,0  8,0

En  [119]:  df1.combine_first(df2)
Fuera[119]:
a b C
0  1,0  NaN  2,0  1  4,0  2,0  
6,0
2  5,0  4,0  10,0
3  3,0  6,0  14,0
4  7,0  8,0  NaN

8.3  Reformar  y  pivotar
Hay  una  serie  de  operaciones  básicas  para  reorganizar  datos  tabulares.  Estas  se  conocen  
alternativamente  como  operaciones  de  remodelación  o  pivote.

242  |  Capítulo  8:  Gestión  de  datos:  unir,  combinar  y  remodelar
Machine Translated by Google

Remodelación  con  indexación  jerárquica  La  

indexación  jerárquica  proporciona  una  forma  coherente  de  reorganizar  los  datos  en  un  DataFrame.
Hay  dos  acciones  principales:

pila
Esto  "rota"  o  gira  de  las  columnas  en  los  datos  a  las  filas

desapilar
Esto  gira  de  las  filas  a  las  columnas.

Ilustraré  estas  operaciones  a  través  de  una  serie  de  ejemplos.  Considere  un  marco  de  datos  
pequeño  con  matrices  de  cadenas  como  índices  de  fila  y  columna:

En  [120]:  data  =  pd.DataFrame(np.arange(6).reshape((2,  3)),  index=pd.Index(['Ohio',  
.....: 'Colorado'],  name='state'),  columnas=pd.Index(['uno',  'dos',  'tres'],  
.....: nombre='número'))
.....:

En  [121]:  datos
Salida[121]:  
número Uno,  dos,  tres
estado
Ohio 0   1 2
Colorado 3 4 5

El  uso  del  método  de  pila  en  estos  datos  pivota  las  columnas  en  las  filas,  produciendo  un
Serie:

En  [122]:  resultado  =  data.stack()

En  [123]:  resultado
Salida[123]:  
estado número
Ohio uno 0
dos   1
tres 2
Colorado  uno  dos  
tres   3  4
dtype:   5
int64

A  partir  de  una  serie  indexada  jerárquicamente,  puede  reorganizar  los  datos  de  nuevo  en  una  serie  de  datos.
Marco  con  desapilar:

En  [124]:  resultado.unstack()
Salida[124]:  
número   Uno,  dos,  tres
estado  
Ohio   0 1 2
Colorado 3 4 5

8.3  Reformar  y  pivotar  |  243
Machine Translated by Google

Por  defecto,  el  nivel  más  interno  está  desapilado  (lo  mismo  con  la  pila).  Puede  desapilar  un  nivel  diferente  pasando  
un  número  o  nombre  de  nivel:

En  [125]:  resultado.unstack(0)
Out[125]:  
número  del  estado  de  Ohio  
Colorado
uno
dos   0  1 3  4
tres 2 5

En  [126]:  result.unstack('estado')
Salida[126]:  
número  del  estado  de  Ohio  
Colorado
uno 0 3
dos 1 4
tres 2 5

Desapilar  podría  introducir  datos  faltantes  si  no  se  encuentran  todos  los  valores  en  el  nivel  en  cada  uno  de  los  
subgrupos:

En  [127]:  s1  =  pd.Series([0,  1,  2,  3],  index=['a',  'b',  'c',  'd'])

En  [128]:  s2  =  pd.Series([4,  5,  6],  index=['c',  'd',  'e'])

En  [129]:  data2  =  pd.concat([s1,  s2],  keys=['one',  'two'])

En  [130]:  datos2
Salida[130]:
uno  un  b
0  1
C 2
d 3
dos  c 4
d 5  

mi 6

tipo:  int64

En  [131]:  data2.unstack()
Fuera[131]:
a b C d mi

uno  0.0  1.0  2.0  3.0  NaN
dos  NaN  NaN  4,0  5,0  6,0

El  apilamiento  filtra  los  datos  que  faltan  de  forma  predeterminada,  por  lo  que  la  operación  es  más  fácil  de  invertir:

En  [132]:  data2.unstack()
Fuera[132]:
a b C d mi

uno  0,0  1,0  2,0  3,0  NaN  dos  NaN  NaN  4,0  
5,0  6,0

244  |  Capítulo  8:  Gestión  de  datos:  unir,  combinar  y  remodelar
Machine Translated by Google

En  [133]:  datos2.unstack().stack()
Fuera[133]:
uno  un 0.0
b 1.0  
C 2.0
d 3.0
dos  c 4.0
d 5.0
mi 6.0  
dtipo:  float64

En  [134]:  data2.unstack().stack(dropna=False)
Fuera[134]:
uno  un   0.0  
b 1.0
C 2.0
d 3.0
mi Yaya
dos  abdominales Yaya
Yaya
C 4.0
d 5.0
mi 6.0
tipo:  float64

Cuando  desapila  en  un  DataFrame,  el  nivel  desapilado  se  convierte  en  el  nivel  más  bajo  en  
el  resultado:

En  [135]:  df  =  pd.DataFrame({'izquierda':  resultado,  'derecha':  resultado  +  5},
.....: columnas=pd.Index(['izquierda',  'derecha'],  nombre='lado'))

En  [136]:  d.f.
Fuera[136]:  
lado izquierda  derecha
estado número
Ohio uno 0 5
dos   1   6  
tres 2 7

colorado  uno 3 8
dos   4 9
tres 5 10

En  [137]:  df.unstack('estado')
Salida[137]:  
lado  izquierdo  derecho  estado  Ohio  
Colorado  Ohio  Colorado
número
uno 0 3 5 8
dos   1 4 6 9
tres 2 5 7 10

Al  llamar  a  stack,  podemos  indicar  el  nombre  del  eje  a  apilar:

8.3  Reformar  y  pivotar  |  245
Machine Translated by Google

En  [138]:  df.unstack('estado').stack('lado')
Salida[138]:  
estado colorado  ohio
número  lado  
uno 3 0
izquierda   8 5
dos 4 1
derecha   9 6
izquierda  derecha   2  

tres  izquierda  derecha5  10 7

Cambio  de  formato  "largo"  a  "ancho"  Una  forma  

común  de  almacenar  varias  series  temporales  en  bases  de  datos  y  CSV  es  en  el  llamado  formato  
largo  o  apilado.  Carguemos  algunos  datos  de  ejemplo  y  hagamos  una  pequeña  cantidad  de  disputas  
de  series  temporales  y  otra  limpieza  de  datos:

En  [139]:  datos  =  pd.read_csv('ejemplos/macrodata.csv')

En  [140]:  datos.head()
Out[140]:  
año  trimestre  realgdp  realcons  realinv  realgovt  realdpi  0  1959.0  1  1959.0 ipc  \  
1.0  2710.349  2.0   1707.4  286.898  470.045  1886.9  28.98  1733.7  310.859  
2778.801 481.301  1919.7  29.15
2  1959.0 3.0  2775.488 1751.8  289.226  491.260  1916.4  29.35
3  1959.0 4.0  2785.204 1753.7  299.356  484.052  1931.3  29.37
4  1960.0 1.0  2847.699 1770.5  331.722  462.199  1955.5  29.54
m1  tbilrate  unemp  0  139.7   pop  inf  realint  5.8  177.146  
2.82 0.00  0.00
1  141.7 3.08 5,1  177.830  2,34 0.74
2  140,5 3.82 5,3  178.657  2,74 1.09
3  140,0 4.33 5,6  179,386  0,27 4.06
4  139.6 3.50 5.2  180.007  2.31 1.19

En  [141]:  períodos  =  pd.PeriodIndex(año=datos.año,  trimestre=datos.trimestre,  nombre='fecha')
.....:

En  [142]:  columnas  =  pd.Index(['realgdp',  'infl',  'unemp'],  name='item')

En  [143]:  datos  =  datos.reindex(columnas=columnas)

En  [144]:  data.index  =  period.to_timestamp('D',  'end')

En  [145]:  ldata  =  data.stack().reset_index().rename(columns={0:  'value'})

Veremos  PeriodIndex  un  poco  más  de  cerca  en  el  Capítulo  11.  En  resumen,  combina  las  
columnas  de  año  y  trimestre  para  crear  una  especie  de  tipo  de  intervalo  de  tiempo.

Ahora,  ldata  se  parece  a:

En  [146]:  ldata[:10]
Fuera[146]:

246  |  Capítulo  8:  Gestión  de  datos:  unir,  combinar  y  remodelar
Machine Translated by Google

fecha  valor  0  1artículo
959­03­31  realgdp  
2710.349
1  1959­03­31 inflación 0.000
2  1959­03­31  5.800  desempleo  
pib  real  3  1959­06­30  
2778.801  4  1959­06­30  2.340
inflación

5  1959­06­30 desempleado 5.100


6  30­09­1959  PIB  real  2775,488  7  30­09­1959  
2,740  8  30­09­1959  5,300   9  31­12­1959  PIB  
inflación

real  2785,204 desempleado

Este  es  el  llamado  formato  largo  para  series  temporales  múltiples  u  otros  datos  de  observación  con  dos  
o  más  claves  (aquí,  nuestras  claves  son  la  fecha  y  el  elemento).  Cada  fila  de  la  tabla  representa  una  
sola  observación.

Los  datos  se  almacenan  con  frecuencia  de  esta  manera  en  bases  de  datos  relacionales  como  MySQL,  
ya  que  un  esquema  fijo  (nombres  de  columna  y  tipos  de  datos)  permite  que  la  cantidad  de  valores  
distintos  en  la  columna  del  elemento  cambie  a  medida  que  se  agregan  datos  a  la  tabla.  En  el  ejemplo  
anterior,  la  fecha  y  el  elemento  normalmente  serían  las  claves  principales  (en  el  lenguaje  de  la  base  de  
datos  relacional),  lo  que  ofrece  integridad  relacional  y  uniones  más  sencillas.  En  algunos  casos,  puede  
ser  más  difícil  trabajar  con  los  datos  en  este  formato;  es  posible  que  prefiera  tener  un  DataFrame  que  
contenga  una  columna  por  valor  de  elemento  distinto  indexado  por  marcas  de  tiempo  en  la  columna  de  
fecha .  El  método  pivote  de  DataFrame  realiza  exactamente  esta  transformación:

En  [147]:  pivoted  =  ldata.pivot('fecha',  'elemento',  'valor')

En  [148]:  pivotado
Salida[148]:  
fecha   inflación realgdp  desempleado
del  artículo
1959­03­31  0.00  2710.349 5.8
1959­06­30  2.34  2778.801  1959­09­30   5.1  
2.74  2775.488  1959­12­31  0.27  2785.204 5.3
5.6
1960­03­31  2.31  2847.699 5.2
1960­06­30  0.14  2834.390 5.2
1960­09­30  2.70  2839.022  1960­12­31   5,6  
1.21  2802.616  1961­03­31  ­0.40  2819.264 6,3  
6,8
1961­06­30  1.47  2872.005 7.0
... ... ... ...
2007­06­30  2,75  13203,977  2007­09­30   4,5  
3,45  13321,109  2007­12­31  6,38   4,7  
13391,249 4,8
2008­03­31  2.82  13366.865 4.9
2008­06­30  8.53  13415.266 5.4
2008­09­30  ­3.16  13324.600  2008­12­31   6,0  
­8.79  13141.920  2009­03­31  0.94   6,9  
12925.410 8,1
2009­06­30  3.37  12901.504 9.2

8.3  Reformar  y  pivotar  |  247
Machine Translated by Google

2009­09­30  3.56  12990.341 9.6
[203  filas  x  3  columnas]

Los  primeros  dos  valores  pasados  son  las  columnas  que  se  usarán  respectivamente  como  índice  
de  fila  y  columna,  luego,  finalmente,  una  columna  de  valor  opcional  para  llenar  el  DataFrame.  
Suponga  que  tiene  dos  columnas  de  valor  que  desea  remodelar  simultáneamente:

En  [149]:  ldata['value2']  =  np.random.randn(len(ldata))

En  [150]:  ldatos[:10]
Out[150]:  
valor  del  elemento  de  fecha  value2  0  
1959­03­31  realgdp  2710.349  0.523772  infl
1  1959­03­31 0.000  0.000940
2  1959­03­31  5.800  1.343810  desempleo  3  1959­06­30  
realgdp  2778.801  ­0.713544  infl  4  1959­06­30  2.340  
­0.831154
5  1959­06­30 desempleado 5.100  ­2.370232
6  1959­09­30  realgdp  2775.488  ­1.860761  7  
1959­09­30  infl  2.740  ­0.860757  8  1959­09­30  5.300  
0.560145  unemp  9  1959­12­31  realgdp  2785.204  
­1.265934

Al  omitir  el  último  argumento,  obtiene  un  DataFrame  con  columnas  jerárquicas:

En  [151]:  pivoted  =  ldata.pivot('fecha',  'elemento')

En  [152]:  pivotado[:5]
Fuera[152]:
value   value2  
fecha   infl  realgdp  unemp infl  realgdp desempleado

del  artículo

1959­03­31  0.00  2710.349  5.8  0.000940  0.523772  1.343810
1959­06­30  2.34  2778.801  5.1  ­0.831154  ­0.713544  ­2.370232
1959­09­30  2.74  2775.488  5.3  ­0.860757  ­1.860761  0.560145  1959­12­31  0.27  
2785.204  5.6  0.119827  ­1.265934  ­1.063512
1960­03­31  2.31  2847.699  5.2  ­2.359419  0.332883  ­0.199543

En  [153]:  pivotado['valor'][:5]
Salida[153]:  
fecha   infl  realgdp  unemp
del  artículo
1959­03­31  0.00  2710.349 5.8
1959­06­30  2.34  2778.801 5.1
1959­09­30  2.74  2775.488   5.3  
1959­12­31  0.27  2785.204 5.6
1960­03­31  2.31  2847.699 5.2

Tenga  en  cuenta  que  pivote  es  equivalente  a  crear  un  índice  jerárquico  usando  set_index  seguido  
de  una  llamada  para  desapilar:

248  |  Capítulo  8:  Gestión  de  datos:  unir,  combinar  y  remodelar
Machine Translated by Google

En  [154]:  sin  apilar  =  ldata.set_index(['fecha',  'elemento']).unstack('elemento')

En  [155]:  desapilado[:7]
Fuera[155]:
value   value2  
fecha   infl  realgdp  unemp infl  realgdp desempleado

del  artículo

1959­03­31  0.00  2710.349  5.8  0.000940  0.523772  1.343810
30­06­1959  2,34  2778,801  5,1  ­0,831154  ­0,713544  ­2,370232  30­09­1959  2,74  2775,488  5,3  
­0,860757  ­1,860761  0,560145
1959­12­31  0.27  2785.204  5.6  0.119827  ­1.265934  ­1.063512
1960­03­31  2.31  2847.699  5.2  ­2.359419  0.332883  ­0.199543
1960­06­30  0.14  2834.390  5.2  ­0.970736  ­1.541996  ­1.307030
1960­09­30  2.70  2839.022  5.6  0.377984  0.286350  ­0.753887

Rotación  de  formato  "ancho"  a  "largo"  Una  

operación  inversa  para  pivotar  para  DataFrames  es  pandas.melt.  En  lugar  de  transformar  una  
columna  en  muchas  en  un  nuevo  DataFrame,  fusiona  múltiples  columnas  en  una,  produciendo  
un  DataFrame  que  es  más  largo  que  la  entrada.  Veamos  un  ejemplo:

En  [157]:  df  =  pd.DataFrame({'clave':  ['foo',  'bar',  'baz'],  'A':  [1,  2,  3],  'B':  [4,  5 ,  6],  'C':  [7,  
.....: 8,  9]})
.....:
.....:

En  [158]:  d.f.
Fuera[158]:
Tecla  ABC  0  1  4  
7  foo  1  2  5  8  bar  2  3  6  
9  baz

La  columna  'clave'  puede  ser  un  indicador  de  grupo,  y  las  otras  columnas  son  valores  de  datos.
Al  usar  pandas.melt,  debemos  indicar  qué  columnas  (si  las  hay)  son  indicadores  de  grupo.  
Usemos  'clave'  como  el  único  indicador  de  grupo  aquí:

En  [159]:  melted  =  pd.melt(df,  ['clave'])

En  [160]:  fundido
Out[160]:  
valor  de  la  variable  clave  0  
foo  1  1  bar  2  baz  3  foo
A
A 2
A 3
B 4
4  bar  5   B 5  

baz  6   B 6

foo  7  bar   C 7
8  baz C 8
C 9

8.3  Reformar  y  pivotar  |  249
Machine Translated by Google

Usando  pivote,  podemos  remodelar  de  nuevo  al  diseño  original:

En  [161]:  reformado  =  melted.pivot('clave',  'variable',  'valor')

En  [162]:  reformado
Out[162]:  
barra  de  teclas  ABC  

variable 2  5  8
baz   3  6  9  1  4  7
foo

Dado  que  el  resultado  de  pivote  crea  un  índice  de  la  columna  utilizada  como  etiquetas  de  fila,  es  posible  que  deseemos  

usar  reset_index  para  mover  los  datos  nuevamente  a  una  columna:

En  [163]:  reformado.reset_index()
Out[163]:  
tecla  variable  ABC  bar  2  5  8  0  
7 baz  3  6  9  foo  1  4  
1
2

También  puede  especificar  un  subconjunto  de  columnas  para  usar  como  columnas  de  valor:

En  [164]:  pd.melt(df,  id_vars=['clave'],  value_vars=['A',  'B'])
Out[164]:  
valor  de  la  variable  clave  0  
A
foo  1  1  bar  2  baz  3  foo
A 2
A 3
B 4
4  bar  5   B 5  

baz B 6

pandas.melt  también  se  puede  usar  sin  ningún  identificador  de  grupo:

En  [165]:  pd.melt(df,  value_vars=['A',  'B',  'C'])
Salida[165]:  
valor  variable  A  1  2
0
1 A
2 A 3
3 B 4
4 B 5  

5 B 6

6 C 7
7 C 8
8 C 9

En  [166]:  pd.melt(df,  value_vars=['clave',  'A',  'B'])
Out[166]:  
tecla  de  valor  variable  
0 barra  de  
1 teclas  foo

250  |  Capítulo  8:  Gestión  de  datos:  unir,  combinar  y  remodelar
Machine Translated by Google

2 baz  clave
3 un  1

4 A 2
5   A
6 B 3  4
7 B 5
8 B 6

8.4  Conclusión
Ahora  que  tiene  algunos  conceptos  básicos  de  pandas  para  la  importación,  limpieza  y  
reorganización  de  datos  en  su  haber,  estamos  listos  para  pasar  a  la  visualización  de  datos  con  
matplotlib.  Volveremos  a  los  pandas  más  adelante  en  el  libro  cuando  hablemos  de  análisis  más  avanzados.

8.4  Conclusión  |  251
Machine Translated by Google
Machine Translated by Google

CAPÍTULO  9

Trazado  y  Visualización

Hacer  visualizaciones  informativas  (a  veces  llamadas  gráficos)  es  una  de  las  tareas  más  importantes  en  el  
análisis  de  datos.  Puede  ser  parte  del  proceso  exploratorio,  por  ejemplo,  para  ayudar  a  identificar  valores  
atípicos  o  transformaciones  de  datos  necesarias,  o  como  una  forma  de  generar  ideas  para  modelos.  Para  
otros,  crear  una  visualización  interactiva  para  la  web  puede  ser  el  objetivo  final.  Python  tiene  muchas  bibliotecas  
complementarias  para  hacer  visualizaciones  estáticas  o  dinámicas,  pero  me  centraré  principalmente  en  
matplotlib  y  las  bibliotecas  que  se  basan  en  él.  matplotlib  es  un  paquete  de  gráficos  de  escritorio  diseñado  

para  crear  gráficos  con  calidad  de  publicación  (principalmente  bidimensionales).  El  proyecto  fue  iniciado  por  
John  Hunter  en  2002  para  habilitar  una  interfaz  de  trazado  similar  a  MATLAB  en  Python.  Las  comunidades  
matplotlib  e  IPython  han  colaborado  para  simplificar  el  trazado  interactivo  desde  el  shell  IPython  (y  ahora,  el  
cuaderno  Jupyter).  matplotlib  es  compatible  con  varios  backends  de  GUI  en  todos  los  sistemas  operativos  y,  
además,  puede  exportar  visualizaciones  a  todos  los  formatos  de  gráficos  vectoriales  y  de  trama  comunes  
(PDF,  SVG,  JPG,  PNG,  BMP,  GIF,  etc.).  Con  la  excepción  de  algunos  diagramas,  casi  todos  los  gráficos  de  
este  libro  se  produjeron  utilizando  matplotlib.

Con  el  tiempo,  matplotlib  ha  generado  una  serie  de  kits  de  herramientas  complementarios  para  la  visualización  
de  datos  que  utilizan  matplotlib  para  su  trazado  subyacente.  Uno  de  ellos  es  el  nacido  en  el  mar,  que  
exploraremos  más  adelante  en  este  capítulo.

La  forma  más  sencilla  de  seguir  los  ejemplos  de  código  del  capítulo  es  utilizar  el  trazado  interactivo  en  el  
cuaderno  de  Jupyter.  Para  configurar  esto,  ejecute  la  siguiente  declaración  en  un  cuaderno  Jupyter:

Cuaderno  %matplotlib

9.1  Una  breve  introducción  a  la  API  de  matplotlib

Con  matplotlib,  usamos  la  siguiente  convención  de  importación:

253
Machine Translated by Google

En  [11]:  importar  matplotlib.pyplot  como  plt

Después  de  ejecutar  %matplotlib  notebook  en  Jupyter  (o  simplemente  %matplotlib  en  IPython),  podemos  intentar  
crear  un  diagrama  simple.  Si  todo  está  configurado  correctamente,  debería  aparecer  un  diagrama  de  líneas  
como  el  de  la  Figura  9­1 :

En  [12]:  importar  numpy  como  np

En  [13]:  datos  =  np.arange(10)

En  [14]:  datos
Salida[14]:  matriz([0,  1,  2,  3,  4,  5,  6,  7,  8,  9])

En  [15]:  plt.plot(datos)

Figura  9­1.  Gráfico  de  línea  simple

Si  bien  las  bibliotecas  como  las  funciones  de  trazado  integradas  de  seaborn  y  pandas  se  ocuparán  de  muchos  
de  los  detalles  mundanos  de  la  creación  de  gráficos,  si  desea  personalizarlos  más  allá  de  las  opciones  de  
función  proporcionadas,  deberá  aprender  un  poco  sobre  la  API  matplotlib.

No  hay  suficiente  espacio  en  el  libro  para  dar  un  tratamiento  integral  a  
la  amplitud  y  profundidad  de  la  funcionalidad  en  matplotlib.  Debería  ser  
suficiente  para  enseñarle  las  cuerdas  para  ponerse  en  marcha.
La  galería  y  la  documentación  de  matplotlib  son  el  mejor  recurso  para  
aprender  funciones  avanzadas.

254  |  Capítulo  9:  Trazado  y  visualización
Machine Translated by Google

Figuras  y  subgráficos  Los  

gráficos  en  matplotlib  residen  dentro  de  un  objeto  Figura .  Puedes  crear  una  nueva  figura  con  
plt.figure:

En  [16]:  fig  =  plt.figure()

En  IPython,  aparecerá  una  ventana  de  trazado  vacía,  pero  en  Jupyter  no  se  mostrará  nada  
hasta  que  usemos  algunos  comandos  más.  plt.figure  tiene  varias  opciones;  en  particular,  
figsize  garantizará  que  la  figura  tenga  cierto  tamaño  y  relación  de  aspecto  si  se  guarda  en  el  disco.

No  puedes  hacer  un  gráfico  con  una  figura  en  blanco.  Tienes  que  crear  una  o  más  subparcelas  
usando  add_subplot:

En  [17]:  ax1  =  fig.add_subplot(2,  2,  1)

Esto  significa  que  la  figura  debe  ser  2  ×  2  (hasta  cuatro  parcelas  en  total),  y  estamos  
seleccionando  la  primera  de  cuatro  subparcelas  (numeradas  desde  1).  Si  crea  las  siguientes  
dos  subtramas,  terminará  con  una  visualización  que  se  parece  a  la  Figura  9­2:

En  [18]:  ax2  =  fig.add_subplot(2,  2,  2)

En  [19]:  ax3  =  fig.add_subplot(2,  2,  3)

Figura  9­2.  Una  figura  matplotlib  vacía  con  tres  subparcelas

9.1  Una  breve  introducción  a  la  API  de  matplotlib  |  255
Machine Translated by Google

Un  matiz  del  uso  de  cuadernos  de  Jupyter  es  que  los  gráficos  se  restablecen  
después  de  evaluar  cada  celda,  por  lo  que  para  gráficos  más  complejos  debe  poner  
todos  los  comandos  de  trazado  en  una  sola  celda  del  cuaderno.

Aquí  ejecutamos  todos  estos  comandos  en  la  misma  celda:

fig  =  plt.figure()  ax1  =  
fig.add_subplot(2,  2,  1)  ax2  =  
fig.add_subplot(2,  2,  2)  ax3  =  
fig.add_subplot(2,  2,  3)

Cuando  ejecuta  un  comando  de  trazado  como  plt.plot([1.5,  3.5,  ­2,  1.6]),  mat   plotlib  se  basa  en  la  última  figura  y  la  
subtrama  utilizada  (creando  una  si  es  necesario),  ocultando  así  la  creación  de  la  figura  y  la  subtrama .  Entonces,  si  
agregamos  el  siguiente  comando,  obtendrá  algo  como  la  Figura  9­3:

En  [20]:  plt.plot(np.random.randn(50).cumsum(),  'k­­')

Figura  9­3.  Visualización  de  datos  después  de  una  sola  parcela

La  'k­­'  es  una  opción  de  estilo  que  indica  a  matplotlib  que  trace  una  línea  discontinua  negra.  Los  objetos  devueltos  por  
fig.add_subplot  aquí  son  objetos  AxesSubplot ,  en  los  que  puede  trazar  directamente  en  las  otras  subtramas  vacías  
llamando  al  método  de  instancia  de  cada  una  (vea  la  Figura  9­4):

256  |  Capítulo  9:  Trazado  y  visualización
Machine Translated by Google

En  [21]:  _  =  ax1.hist(np.random.randn(100),  bins=20,  color='k',  alpha=0.3)

En  [22]:  ax2.scatter(np.arange(30),  np.arange(30)  +  3  *  np.random.randn(30))

Figura  9­4.  Visualización  de  datos  después  de  parcelas  adicionales

Puede  encontrar  un  catálogo  completo  de  tipos  de  gráficos  en  la  documentación  de  matplotlib.

Crear  una  figura  con  una  cuadrícula  de  subtramas  es  una  tarea  muy  común,  por  lo  que  matplotlib  incluye  un  
método  conveniente,  plt.subplots,  que  crea  una  nueva  figura  y  devuelve  una  matriz  NumPy  que  contiene  los  
objetos  de  subtrama  creados:

En  [24]:  fig,  ejes  =  plt.subplots(2,  3)

In  [25]:  ejes  
Out[25]:  
array([[<  objeto  matplotlib.axes._subplots.AxesSubplot  en  0x7fb626374048>,  <  objeto  
matplotlib.axes._subplots.AxesSubplot  en  0x7fb62625db00>,  <  objeto  
matplotlib.axes._subplots.AxesSubplot  en  0x7fb6262f6c88>],  [<  objeto  
matplotlib.axes._subplots.AxesSubplot  en  0x7fb6261a36a0>,  <  objeto  
matplotlib.axes._subplots.AxesSubplot  en  0x7fb626181860>,  <  objeto  
matplotlib.axes._subplots.AxesSubplot  en  0  x7fb6260fd4e0>]],  dtype  =objeto )

Esto  es  muy  útil,  ya  que  la  matriz  de  ejes  se  puede  indexar  fácilmente  como  una  matriz  bidimensional;  por  ejemplo,  
ejes[0,  1].  También  puede  indicar  que  las  subparcelas  deben  tener  el  mismo  eje  x  o  y  usando  sharex  y  sharey,  
respectivamente.  Esto  es  especialmente  útil  cuando  compara  datos  en  la  misma  escala;  de  lo  contrario,  matplotlib  
escala  automáticamente  los  límites  de  la  trama  de  forma  independiente.  Consulte  la  Tabla  9­1  para  obtener  más  
información  sobre  este  método.

9.1  Una  breve  introducción  a  la  API  de  matplotlib  |  257
Machine Translated by Google

Tabla  9­1.  opciones  de  pyplot.subplots

Argumento Descripción

filas Número  de  filas  de  subparcelas

ncoles Número  de  columnas  de  subparcelas

compartir Todas  las  subparcelas  deben  usar  las  mismas  marcas  del  eje  x  (el  ajuste  de  xlim  afectará  a  todas  las  subparcelas)

compartir Todas  las  subparcelas  deben  usar  las  mismas  marcas  del  eje  y  (ajustar  el  ylim  afectará  a  todas  las  subparcelas)

subplot_kw  Dict  de  palabras  clave  pasadas  a  la  llamada  add_subplot  utilizada  para  crear  
cada  subplot  **fig_kw  Se  usan  palabras  clave  adicionales  para  subplots  al  crear  la  figura,  como  plt.subplots(2,  2,
tamaño  de  figura  =  (8,  6))

Ajustar  el  espaciado  alrededor  de  las  

subparcelas  Por  defecto,  matplotlib  deja  una  cierta  cantidad  de  relleno  alrededor  del  exterior  de  
las  subparcelas  y  el  espacio  entre  las  subparcelas.  Este  espaciado  se  especifica  en  relación  con  
la  altura  y  el  ancho  del  gráfico,  de  modo  que  si  cambia  el  tamaño  del  gráfico  mediante  
programación  o  manualmente  mediante  la  ventana  GUI,  el  gráfico  se  ajustará  dinámicamente.  
Puede  cambiar  el  espaciado  usando  el  método  subplots_adjust  en  los  objetos  Figura ,  también  
disponible  como  una  función  de  nivel  superior:

subplots_adjust(izquierda=Ninguno,  abajo=Ninguno,  derecha=Ninguno,  arriba=Ninguno,  
wspace=Ninguno,  hspace=Ninguno)

wspace  y  hspace  controlan  el  porcentaje  del  ancho  de  la  figura  y  la  altura  de  la  figura,  
respectivamente,  para  usar  como  espaciado  entre  las  subparcelas.  Aquí  hay  un  pequeño  ejemplo  
donde  reduzco  el  espacio  hasta  cero  (vea  la  Figura  9­5):

fig,  ejes  =  plt.subplots(2,  2,  sharex=True,  sharey=True)  for  i  in  range(2):  for  j  in  
range(2):  ejes[i,  
j].hist(np.random.randn  
(500),  contenedores=50,  color='k',  alfa=0.5)
plt.subplots_adjust(wspace=0,  hspace=0)

258  |  Capítulo  9:  Trazado  y  visualización
Machine Translated by Google

Figura  9­5.  Visualización  de  datos  sin  espacio  entre  subparcelas

Puede  notar  que  las  etiquetas  de  los  ejes  se  superponen.  matplotlib  no  verifica  si  las  etiquetas  se  
superponen,  por  lo  que,  en  un  caso  como  este,  debe  corregir  las  etiquetas  usted  mismo  especificando  
ubicaciones  de  marca  explícitas  y  etiquetas  de  marca  (veremos  cómo  hacerlo  en  las  siguientes  
secciones). ).

Colores,  marcadores  y  estilos  de  línea  La  

función  de  trazado  principal  de  Matplotlib  acepta  matrices  de  coordenadas  x  e  y  y,  opcionalmente,  una  
abreviatura  de  cadena  que  indica  el  color  y  el  estilo  de  línea.  Por  ejemplo,  para  graficar  x  versus  y  con  
guiones  verdes,  ejecutaría:

ax.plot(x,  y,  'g­­')

Esta  forma  de  especificar  tanto  el  color  como  el  estilo  de  línea  en  una  cadena  se  proporciona  por  
conveniencia;  en  la  práctica,  si  estuviera  creando  tramas  programáticamente,  es  posible  que  prefiera  no  
tener  que  juntar  cadenas  para  crear  tramas  con  el  estilo  deseado.  La  misma  trama  también  podría  
haberse  expresado  más  explícitamente  como:

ax.plot(x,  y,  estilo  de  línea='­­',  color='g')

Se  proporcionan  varias  abreviaturas  de  colores  para  los  colores  de  uso  común,  pero  puede  usar  cualquier  
color  del  espectro  especificando  su  código  hexadecimal  (p.  ej.,  '  #CECECE').
Puede  ver  el  conjunto  completo  de  estilos  de  línea  mirando  la  cadena  de  documentos  para  plot  (¿usar  
plot?  en  IPython  o  Jupyter).

9.1  Una  breve  introducción  a  la  API  de  matplotlib  |  259
Machine Translated by Google

Los  gráficos  de  líneas  también  pueden  tener  marcadores  para  resaltar  los  puntos  de  datos  reales.  Dado  que  
matplotlib  crea  un  gráfico  de  líneas  continuas,  interpolando  entre  puntos,  en  ocasiones  puede  no  estar  claro  dónde  
se  encuentran  los  puntos.  El  marcador  puede  ser  parte  de  la  cadena  de  estilo,  que  debe  tener  un  color  seguido  del  
tipo  de  marcador  y  el  estilo  de  línea  (consulte  la  Figura  9­6):

En  [30]:  from  numpy.random  import  randn

En  [31]:  plt.plot(randn(30).cumsum(),  'ko­­')

Figura  9­6.  Gráfico  de  líneas  con  marcadores

Esto  también  podría  haberse  escrito  más  explícitamente  como:

plot(randn(30).cumsum(),  color='k',  estilo  de  línea='discontinua',  marcador='o')

Para  los  gráficos  de  líneas,  notará  que  los  puntos  subsiguientes  se  interpolan  linealmente  de  forma  predeterminada.  
Esto  se  puede  modificar  con  la  opción  de  estilo  de  dibujo  (Figura  9­7):

En  [33]:  datos  =  np.random.randn(30).cumsum()

En  [34]:  plt.plot(data,  'k­­',  label='Default')
Salida[34]:  [<matplotlib.lines.Line2D  en  0x7fb624d86160>]

En  [35]:  plt.plot(data,  'k­',  drawstyle='steps­post',  label='steps­post')
Salida[35]:  [<matplotlib.lines.Line2D  en  0x7fb624d869e8>]

En  [36]:  plt.legend(loc='mejor')

260  |  Capítulo  9:  Trazado  y  visualización
Machine Translated by Google

Figura  9­7.  Trazado  de  líneas  con  diferentes  opciones  de  estilo  de  dibujo

Puede  notar  una  salida  como  <matplotlib.lines.Line2D  at ...>  cuando  ejecuta  esto.  matplotlib  devuelve  objetos  que  hacen  
referencia  al  subcomponente  de  trama  que  se  acaba  de  agregar.
La  mayor  parte  del  tiempo  puede  ignorar  con  seguridad  esta  salida.  Aquí,  dado  que  pasamos  los  argumentos  de  la  
etiqueta  a  plot,  podemos  crear  una  leyenda  de  plot  para  identificar  cada  línea  usando  plt.legend.

Debe  llamar  a  plt.legend  (o  ax.legend,  si  tiene  una  referencia  a  
los  ejes)  para  crear  la  leyenda,  haya  pasado  o  no  las  opciones  
de  etiqueta  al  trazar  los  datos.

Marcas,  etiquetas  y  leyendas  Para  la  mayoría  

de  los  tipos  de  decoraciones  de  gráficos,  hay  dos  formas  principales  de  hacer  las  cosas:  usar  la  interfaz  de  pyplot  
procedimental  (es  decir,  matplotlib.pyplot)  y  la  API  matplotlib  nativa  más  orientada  a  objetos.

La  interfaz  de  pyplot ,  diseñada  para  uso  interactivo,  consta  de  métodos  como  xlim,  xticks  y  xticklabels.  Éstos  controlan  el  
rango  de  trazado,  las  ubicaciones  de  las  marcas  y  las  etiquetas  de  las  marcas,  respectivamente.  Se  pueden  utilizar  de  
dos  formas:

9.1  Una  breve  introducción  a  la  API  de  matplotlib  |  261
Machine Translated by Google

•  Llamado  sin  argumentos  devuelve  el  valor  del  parámetro  actual  (p.  ej.,  plt.xlim()  devuelve  el  rango  de  
trazado  del  eje  x  actual)

•  Llamado  con  parámetros  establece  el  valor  del  parámetro  (p.  ej.,  plt.xlim([0,  10]),  establece  el  rango  
del  eje  x  de  0  a  10)

Todos  estos  métodos  actúan  en  el  AxesSubplot  activo  o  creado  más  recientemente.  Cada  uno  de  ellos  
corresponde  a  dos  métodos  en  el  propio  objeto  de  la  subtrama;  en  el  caso  de  xlim,  estos  son  ax.get_xlim  
y  ax.set_xlim.  Prefiero  usar  los  métodos  de  instancia  de  la  trama  secundaria  para  ser  explícito  (y  
especialmente  cuando  trabajo  con  varias  tramas  secundarias),  pero  ciertamente  puede  usar  el  que  le  
resulte  más  conveniente.

Establecer  el  título,  las  etiquetas  de  los  ejes,  las  marcas  y  las  etiquetas  de  marcas

Para  ilustrar  la  personalización  de  los  ejes,  crearé  una  figura  simple  y  un  diagrama  de  un  recorrido  
aleatorio  (consulte  la  Figura  9­8):

En  [37]:  fig  =  plt.figura()

En  [38]:  hacha  =  fig.add_subplot(1,  1,  1)

En  [39]:  ax.plot(np.random.randn(1000).cumsum())

Figura  9­8.  Trama  simple  para  ilustrar  xticks  (con  etiqueta)

Para  cambiar  las  marcas  del  eje  x,  es  más  fácil  usar  set_xticks  y  set_xticklabels.  El  primero  le  indica  a  
matplotlib  dónde  colocar  las  marcas  a  lo  largo  del  rango  de  datos;  por  defecto

262  |  Capítulo  9:  Trazado  y  visualización
Machine Translated by Google

estas  ubicaciones  también  serán  las  etiquetas.  Pero  podemos  establecer  cualquier  otro  valor  como  etiquetas  
usando  set_xticklabels:

En  [40]:  marcas  =  ax.set_xticks([0,  250,  500,  750,  1000])

En  [41]:  etiquetas  =  ax.set_xticklabels(['uno',  'dos',  'tres',  'cuatro',  'cinco'],  rotación=30,  tamaño  
.....: de  fuente='pequeño')

La  opción  de  rotación  establece  las  etiquetas  de  marca  x  en  una  rotación  de  30  grados.  Por  último,  set_xlabel  le  
da  un  nombre  al  eje  x  y  set_title  el  título  de  la  subparcela  (vea  la  Figura  9­9  para  ver  la  figura  resultante):

En  [42]:  ax.set_title('Mi  primera  trama  matplotlib')
Salida[42]:  <matplotlib.text.Text  en  0x7fb624d055f8>

En  [43]:  ax.set_xlabel('Etapas')

Figura  9­9.  Trama  simple  para  ilustrar  xticks

Modificar  el  eje  y  consiste  en  el  mismo  proceso,  sustituyendo  y  por  x  en  lo  anterior.
La  clase  de  ejes  tiene  un  método  establecido  que  permite  la  configuración  por  lotes  de  las  propiedades  del  gráfico.  
Del  ejemplo  anterior,  también  podríamos  haber  escrito:

props  =  
{ 'title':  'Mi  primera  trama  matplotlib',  'xlabel':  
'Etapas'

}  ax.set(**accesorios)

9.1  Una  breve  introducción  a  la  API  de  matplotlib  |  263
Machine Translated by Google

Adición  de  

leyendas  Las  leyendas  son  otro  elemento  crítico  para  identificar  los  elementos  de  la  trama.  Hay  un  par  
de  maneras  de  agregar  uno.  Lo  más  fácil  es  pasar  el  argumento  de  la  etiqueta  al  agregar  cada  parte  de  
la  trama:

En  [44]:  from  numpy.random  import  randn

En  [45]:  fig  =  plt.figure();  hacha  =  fig.add_subplot(1,  1,  1)

En  [46]:  ax.plot(randn(1000).cumsum(),  'k',  label='one')
Salida[46]:  [<matplotlib.lines.Line2D  en  0x7fb624bdf860>]

En  [47]:  ax.plot(randn(1000).cumsum(),  'k­­',  label='two')
Salida[47]:  [<matplotlib.lines.Line2D  en  0x7fb624be90f0>]

En  [48]:  ax.plot(randn(1000).cumsum(),  'k.',  label='tres')
Salida[48]:  [<matplotlib.lines.Line2D  en  0x7fb624be9160>]

Una  vez  que  haya  hecho  esto,  puede  llamar  a  ax.legend()  o  plt.legend()  para  crear  automáticamente  una  
leyenda.  El  gráfico  resultante  se  muestra  en  la  Figura  9­10:

En  [49]:  ax.legend(loc='mejor')

Figura  9­10.  Trama  simple  con  tres  líneas  y  leyenda

El  método  de  la  leyenda  tiene  otras  opciones  para  el  argumento  loc  ubicación .  Consulte  la  cadena  de  
documentación  (¿con  ax.legend?)  para  obtener  más  información.

264  |  Capítulo  9:  Trazado  y  visualización
Machine Translated by Google

El  loc  le  dice  a  matplotlib  dónde  colocar  el  gráfico.  Si  no  eres  quisquilloso,  'mejor'  es  una  buena  
opción,  ya  que  elegirá  una  ubicación  que  esté  más  apartada.  Para  excluir  uno  o  más  elementos  de  
la  leyenda,  pase  no  label  o  label='_nolegend_'.

Anotaciones  y  dibujo  en  una  subparcela  Además  de  

los  tipos  de  parcela  estándar,  es  posible  que  desee  dibujar  sus  propias  anotaciones  de  parcela,  
que  pueden  consistir  en  texto,  flechas  u  otras  formas.  Puede  agregar  anotaciones  y  texto  usando  
las  funciones  de  texto,  flecha  y  anotar .  text  dibuja  texto  en  las  coordenadas  dadas  (x,  y)  en  el  
gráfico  con  un  estilo  personalizado  opcional:

ax.text(x,  y,  '¡Hola  mundo!',  
family='monospace',  fontsize=10)

Las  anotaciones  pueden  dibujar  tanto  el  texto  como  las  flechas  dispuestas  adecuadamente.  Como  
ejemplo,  tracemos  el  precio  de  cierre  del  índice  S&P  500  desde  2007  (obtenido  de  Yahoo!  Finance)  
y  anotemos  algunas  de  las  fechas  importantes  de  la  crisis  financiera  de  2008­2009.
Puede  reproducir  más  fácilmente  este  ejemplo  de  código  en  una  sola  celda  en  un  cuaderno  Jupyter.  
Vea  la  Figura  9­11  para  ver  el  resultado:

desde  fechahora  fechahora  de  importación

fig  =  plt.figure()  hacha  
=  fig.add_subplot(1,  1,  1)

datos  =  pd.read_csv('ejemplos/spx.csv',  index_col=0,  parse_dates=True)  spx  =  
datos['SPX']

spx.plot(ax=ax,  estilo='k­')

crisis_data  =  
[ (datetime(2007,  10,  11),  'Pico  del  mercado  alcista'),  
(datetime(2008,  3,  12),  'Bear  Stearns  falla'),  
(datetime(2008,  9,  15),  'Lehman  Bancarrota')
]

para  fecha,  etiqueta  en  crisis_data:  
ax.annotate(label,  xy=(fecha,  spx.asof(fecha)  +  75),  
xytext=(fecha,  spx.asof(fecha)  +  225),  
arrowprops=dict(facecolor='  negro',  ancho  de  cabeza=4,  ancho=2,
headlength=4),  
horizontalalignment='izquierda',  verticalalignment='superior')

#  Zoom  sobre  2007­2010
hacha.set_xlim(['1/1/2007',  '1/1/2011'])  
hacha.set_ylim([600,  1800])

ax.set_title('  Fechas  importantes  en  la  crisis  financiera  de  2008­2009')

9.1  Una  breve  introducción  a  la  API  de  matplotlib  |  265
Machine Translated by Google

Figura  9­11.  Fechas  importantes  en  la  crisis  financiera  de  2008­2009

Hay  un  par  de  puntos  importantes  para  resaltar  en  este  gráfico:  el  método  ax.annotate  puede  dibujar  etiquetas  en  las  
coordenadas  x  e  y  indicadas.  Usamos  los  métodos  set_xlim  y  set_ylim  para  establecer  manualmente  los  límites  de  
inicio  y  final  de  la  trama  en  lugar  de  usar  el  valor  predeterminado  de  matplotlib.  Por  último,  ax.set_title  agrega  un  
título  principal  a  la  trama.

Consulte  la  galería  en  línea  de  matplotlib  para  obtener  muchos  más  ejemplos  de  anotaciones  de  los  que  aprender.

Dibujar  formas  requiere  algo  más  de  cuidado.  matplotlib  tiene  objetos  que  representan  muchas  formas  comunes,  
denominadas  parches.  Algunos  de  estos,  como  Rectangle  y  Circle,  se  encuentran  en  matplotlib.pyplot,  pero  el  
conjunto  completo  se  encuentra  en  matplotlib.patches.

Para  agregar  una  forma  a  un  gráfico,  cree  el  objeto  de  parche  shp  y  agréguelo  a  un  subgráfico  llamando  
ax.add_patch(shp)  (vea  la  Figura  9­12):

fig  =  plt.figure()  hacha  
=  fig.add_subplot(1,  1,  1)

rect  =  plt.Rectangle((0.2,  0.75),  0.4,  0.15,  color='k',  alpha=0.3)  circ  =  plt.Circle((0.7,  
0.2),  0.15,  color='b',  alpha=0.3)  pgon  =  plt.Polígono([[0.15,  0.15],  
[0.35,  0.4],  [0.2,  0.6]],  color='g',  alfa=0.5)

ax.add_patch(rect)  
ax.add_patch(circ)  
ax.add_patch(pgon)

266  |  Capítulo  9:  Trazado  y  visualización
Machine Translated by Google

Figura  9­12.  Visualización  de  datos  compuesta  por  tres  parches  diferentes

Si  observa  la  implementación  de  muchos  tipos  de  gráficos  familiares,  verá  que  se  ensamblan  a  partir  de  parches.

Guardar  gráficos  en  un  archivo  

Puede  guardar  la  figura  activa  en  un  archivo  usando  plt.savefig.  Este  método  es  equivalente  al  método  de  
instancia  savefig  del  objeto  figure .  Por  ejemplo,  para  guardar  una  versión  SVG  de  una  figura,  solo  necesita  
escribir:

plt.savefig('figpath.svg')

El  tipo  de  archivo  se  deduce  de  la  extensión  del  archivo.  Entonces,  si  usara .pdf  en  su  lugar,  obtendría  un  PDF.  
Hay  un  par  de  opciones  importantes  que  utilizo  con  frecuencia  para  publicar  gráficos:  dpi,  que  controla  la  
resolución  de  puntos  por  pulgada,  y  bbox_inches,  que  puede  recortar  el  espacio  en  blanco  alrededor  de  la  figura  
real.  Para  obtener  el  mismo  gráfico  que  un  PNG  con  un  espacio  en  blanco  mínimo  alrededor  del  gráfico  y  a  400  
DPI,  haría  lo  siguiente:

plt.savefig('figpath.png',  dpi=400,  bbox_inches='apretado')

savefig  no  tiene  que  escribir  en  el  disco;  también  puede  escribir  en  cualquier  objeto  similar  a  un  archivo,  como  un
BytesIO:

from  io  import  BytesIO  
buffer  =  BytesIO()  
plt.savefig(buffer)  
plot_data  =  buffer.getvalue()

Consulte  la  Tabla  9­2  para  obtener  una  lista  de  algunas  otras  opciones  para  savefig.

9.1  Una  breve  introducción  a  la  API  de  matplotlib  |  267
Machine Translated by Google

Tabla  9­2.  Opciones  de  figure.savefig

Argumento Descripción  

fnombre Cadena  que  contiene  una  ruta  de  archivo  o  un  objeto  similar  a  un  archivo  de  Python.  El  formato  de  la  figura  se  deduce  de  

la  extensión  del  archivo  (p.  ej., .pdf  para  PDF  o .png  para  PNG)

ppp La  resolución  de  la  figura  en  puntos  por  pulgada;  el  valor  predeterminado  es  100,  pero  se  puede  configurar  El  

color  de  cara,   color  del  fondo  de  la  figura  fuera  de  las  subtramas;  'w' (blanco),  por  defecto

color  de  borde
formato El  formato  de  archivo  explícito  a  usar  ('png',  'pdf',  'svg',  'ps',  'eps', ...)

bbox_pulgadas La  porción  de  la  figura  a  guardar;  si  se  pasa  'apretado' ,  intentará  recortar  el  espacio  vacío  alrededor  de  la  figura

Configuración  de  matplotlib  matplotlib  

viene  configurado  con  esquemas  de  color  y  valores  predeterminados  que  están  orientados  principalmente  a  la  
preparación  de  figuras  para  su  publicación.  Afortunadamente,  casi  todo  el  comportamiento  predeterminado  se  
puede  personalizar  a  través  de  un  amplio  conjunto  de  parámetros  globales  que  rigen  el  tamaño  de  la  figura,  el  
espaciado  de  las  subtramas,  los  colores,  los  tamaños  de  fuente,  los  estilos  de  cuadrícula,  etc.  Una  forma  de  
modificar  la  configuración  mediante  programación  desde  Python  es  usar  el  método  rc ;  por  ejemplo,  para  
establecer  el  tamaño  de  figura  predeterminado  global  en  10  ×  10,  puede  ingresar:

plt.rc('figura',  figsize=(10,  10))

El  primer  argumento  para  rc  es  el  componente  que  desea  personalizar,  como  'figura',  'ejes',  
'xtick',  'ytick',  'grid',  'leyenda'  o  muchos  otros.  Después  de  eso  puede  seguir  una  secuencia  de  
argumentos  de  palabras  clave  que  indican  los  nuevos  parámetros.  Una  manera  fácil  de  
escribir  las  opciones  en  su  programa  es  como  un  dictado:

font_options  =  {'familia' :  'monoespacio',  'peso' :  'negrita',  
'tamaño' :  'pequeño'}  
plt.rc('fuente',  **font_options)

Para  una  personalización  más  amplia  y  para  ver  una  lista  de  todas  las  opciones,  matplotlib  viene  con  un  archivo  
de  configuración  matplotlibrc  en  el  directorio  matplotlib/mpl­data.  Si  personaliza  este  archivo  y  lo  coloca  en  su  
directorio  de  inicio  titulado .matplotlibrc,  se  cargará  cada  vez  que  use  matplotlib.

Como  veremos  en  la  siguiente  sección,  el  paquete  seaborn  tiene  varios  estilos  o  temas  de  trama  incorporados  
que  usan  el  sistema  de  configuración  de  matplotlib  internamente.

9.2  Trazado  con  pandas  y  seaborn
matplotlib  puede  ser  una  herramienta  de  nivel  bastante  bajo.  Un  gráfico  se  ensambla  a  partir  de  sus  
componentes  básicos:  la  visualización  de  datos  (es  decir,  el  tipo  de  gráfico:  línea,  barra,  cuadro,  dispersión,  
contorno,  etc.),  leyenda,  título,  etiquetas  de  marca  y  otras  anotaciones.

268  |  Capítulo  9:  Trazado  y  visualización
Machine Translated by Google

En  pandas  podemos  tener  múltiples  columnas  de  datos,  junto  con  etiquetas  de  fila  y  columna.  Pandas  tiene  
métodos  integrados  que  simplifican  la  creación  de  visualizaciones  a  partir  de  objetos  de  marco  de  datos  y  
serie.  Otra  biblioteca  es  seaborn,  una  biblioteca  de  gráficos  estadísticos  creada  por  Michael  Waskom.  
Seaborn  simplifica  la  creación  de  muchos  tipos  de  visualización  comunes.

La  importación  de  seaborn  modifica  los  esquemas  de  color  predeterminados  de  
matplotlib  y  los  estilos  de  trazado  para  mejorar  la  legibilidad  y  la  estética.  Incluso  
si  no  utiliza  la  API  de  seaborn,  es  posible  que  prefiera  importar  seaborn  como  
una  forma  sencilla  de  mejorar  la  estética  visual  de  los  diagramas  generales  de  
matplotlib.

Gráficos  de  línea

Series  y  DataFrame  tienen  cada  uno  un  atributo  de  gráfico  para  crear  algunos  tipos  de  gráficos  básicos.  
Por  defecto,  plot()  hace  diagramas  de  líneas  (vea  la  Figura  9­13):

En  [60]:  s  =  pd.Series(np.random.randn(10).cumsum(),  index=np.arange(0,  100,  10))

En  [61]:  s.plot()

Figura  9­13.  Gráfico  de  serie  simple

El  índice  del  objeto  Serie  se  pasa  a  matplotlib  para  trazar  en  el  eje  x,  aunque  puede  deshabilitarlo  pasando  
use_index=False.  Los  ticks  y  límites  del  eje  x  se  pueden  ajustar  con  las  opciones  xticks  y  xlim ,  y  el  eje  y  
respectivamente  con  yticks  y

9.2  Trazado  con  pandas  y  seaborn  |  269
Machine Translated by Google

ylim.  Consulte  la  Tabla  9­3  para  obtener  una  lista  completa  de  las  opciones  de  trazado .  Comentaré  algunos  más  
a  lo  largo  de  esta  sección  y  dejaré  que  usted  explore  el  resto.

La  mayoría  de  los  métodos  de  trazado  de  pandas  aceptan  un  parámetro  ax  opcional ,  que  puede  ser  un  objeto  
de  subtrama  matplotlib.  Esto  le  brinda  una  ubicación  más  flexible  de  las  subparcelas  en  un  diseño  de  cuadrícula.

El  método  de  trazado  de  DataFrame  traza  cada  una  de  sus  columnas  como  una  línea  diferente  en  el  mismo  
subgráfico,  creando  una  leyenda  automáticamente  (vea  la  Figura  9­14):

En  [62]:  df  =  pd.DataFrame(np.random.randn(10,  4).cumsum(0),
.....: columnas=['A',  'B',  'C',  'D'],  
.....: índice=np.arange(0,  100,  10))

En  [63]:  df.plot()

Figura  9­14.  Gráfico  de  marco  de  datos  simple

El  atributo  plot  contiene  una  "familia"  de  métodos  para  diferentes  tipos  de  parcelas.  Por  ejemplo,  df.plot()  es  
equivalente  a  df.plot.line().  Exploraremos  algunos  de  estos  métodos.
próximo.

Los  argumentos  de  palabras  clave  adicionales  para  trazar  se  pasan  a  la  
respectiva  función  de  trazado  de  matplotlib,  por  lo  que  puede  personalizar  
aún  más  estos  gráficos  aprendiendo  más  sobre  la  API  de  matplotlib.

270  |  Capítulo  9:  Trazado  y  visualización
Machine Translated by Google

Tabla  9­3.  Argumentos  del  método  Series.plot

Argumento Descripción

etiqueta Etiqueta  para  la  leyenda  

hacha de  la  trama  matplotlib  subplot  object  para  trazar;  si  no  pasa  nada,  usa  la  subtrama  matplotlib  activa

estilo Cadena  de  estilo,  como  'ko­­',  para  pasar  a  matplotlib

alfa La  trama  llena  la  opacidad  (de  0  a  1)

amable Puede  ser  'área',  'barra',  'barh',  'densidad',  'hist',  'kde',  'línea',  'pastel'

Utilice  la  escala  logarítmica  en  el  eje  y  logy  

use_index  Utilice  el  índice  de  objetos  para  las  etiquetas  de  marca

putrefacción Rotación  de  etiquetas  de  ticks  (0  a  360)

xticks Valores  a  usar  para  las  marcas  del  eje  x

garrapatas Valores  a  usar  para  las  marcas  del  eje  y  

xlim Límites  del  eje  x  (p.  ej.,  [0,  10])  Límites  del  eje  

y  Cuadrícula  del  

cuadrícula  ylim eje  de  visualización  (activada  de  forma  predeterminada)

DataFrame  tiene  una  serie  de  opciones  que  permiten  cierta  flexibilidad  en  la  forma  en  que  se  manejan  
las  columnas;  por  ejemplo,  si  trazarlos  todos  en  la  misma  subparcela  o  crear  subparcelas  separadas.  
Consulte  la  Tabla  9­4  para  obtener  más  información  sobre  estos.

Tabla  9­4.  Argumentos  de  trazado  específicos  de  DataFrame

Argumentos   Descripción

secundarios Trazar  cada  columna  de  DataFrame  en  una  subtrama  separada

compartir Si  subplots=True,  comparte  el  mismo  eje  x,  vinculando  marcas  y  límites

compartir Si  subplots=True,  comparte  el  mismo  eje  y

tamaño  de  higo Tamaño  de  la  figura  a  crear  como  tupla

título Trazar  título  como  cadena

legend   Agregar  una  leyenda  de  subtrama  (Verdadero  por  defecto)

sort_columns  Trazar  columnas  en  orden  alfabético;  por  defecto  usa  el  orden  de  las  columnas  existente

Para  el  trazado  de  series  de  tiempo,  consulte  el  Capítulo  11.

9.2  Trazado  con  pandas  y  seaborn  |  271
Machine Translated by Google

Parcelas  de  barras

plot.bar  ()  y  plot.barh()  hacen  gráficos  de  barras  verticales  y  horizontales,  respectivamente.  En  este  caso,  el  
índice  Series  o  DataFrame  se  usará  como  las  marcas  x  (barra)  o  y  (barh)  (vea  la  Figura  9­15):

En  [64]:  fig,  ejes  =  plt.subplots(2,  1)

En  [65]:  datos  =  pd.Series(np.random.rand(16),  index=list('abcdefghijklmnop'))

En  [66]:  data.plot.bar(ax=axes[0],  color='k',  alpha=0.7)
Salida[66]:  <matplotlib.axes._subplots.AxesSubplot  en  0x7fb62493d470>

En  [67]:  data.plot.barh(ax=axes[1],  color='k',  alpha=0.7)

Figura  9­15.  Gráfica  de  barras  horizontales  y  verticales

Las  opciones  color='k'  y  alpha=0.7  establecen  el  color  de  los  gráficos  en  negro  y  usan  transparencia  parcial  
en  el  relleno.

272  |  Capítulo  9:  Trazado  y  visualización
Machine Translated by Google

Con  un  DataFrame,  los  diagramas  de  barras  agrupan  los  valores  de  cada  fila  en  un  grupo  de  barras,  una  al  lado  de  la  otra,  para  cada  valor.  

Consulte  la  Figura  9­16:

En  [69]:  df  =  pd.DataFrame(np.random.rand(6,  4),
.....: index=['uno',  'dos',  'tres',  'cuatro',  'cinco',  'seis'],  column=pd.Index(['A',  'B',  'C',  
.....: 'D' ],  nombre='Género'))

En  [70]:  d.f.
Fuera[70]:
Género A B C D
uno 0.370670  0.602792  0.229159  0.486744  0.420082  
0.571653  0.049024  0.880592  dos  tres  0.814568  0.277160  
0.880316  0.431326  cuatro  0.374020  0  .899420  0.460304  0.100843  
cinco  0.433270  0.125107  0.494675  0.961825

seis 0,601648  0,478576  0,205690  0,560547

En  [71]:  df.plot.bar()

Figura  9­16.  Diagrama  de  barras  de  marco  de  datos

Tenga  en  cuenta  que  el  nombre  "Género"  en  las  columnas  de  DataFrame  se  usa  para  titular  la  leyenda.

9.2  Trazado  con  pandas  y  seaborn  |  273
Machine Translated by Google

Creamos  diagramas  de  barras  apiladas  a  partir  de  un  DataFrame  pasando  stacked=True,  lo  que  da  como  resultado  que  el  
valor  de  cada  fila  se  apila  (consulte  la  Figura  9­17):

En  [73]:  df.plot.barh(apilado=Verdadero,  alfa=0.5)

Figura  9­17.  Diagrama  de  barras  apiladas  de  DataFrame

Una  receta  útil  para  los  diagramas  de  barras  es  visualizar  la  frecuencia  del  
valor  de  una  Serie  usando  value_counts:  s.value_counts().plot.bar().

Volviendo  al  conjunto  de  datos  de  propinas  utilizado  anteriormente  en  el  libro,  supongamos  que  queremos  hacer  un  gráfico  
de  barras  apiladas  que  muestre  el  porcentaje  de  puntos  de  datos  para  cada  tamaño  de  grupo  en  cada  día.  Cargo  los  datos  
usando  read_csv  y  hago  una  tabulación  cruzada  por  día  y  tamaño  del  grupo:

En  [75]:  consejos  =  pd.read_csv('ejemplos/consejos.csv')

En  [76]:  party_counts  =  pd.crosstab(consejos['día'],  consejos['tamaño'])

En  [77]:  party_counts
Fuera[77]:  
tamaño  1  2  3  4  5  6  día

Vie  1  16  1 1  0  0
sáb  2  53  18  13  1  0
dom  0  39  15  18  3  1
Jue  1  48  4  5  1  3

274  |  Capítulo  9:  Trazado  y  visualización
Machine Translated by Google

#  No  hay  muchas  fiestas  de  1  y  6  personas  En  
[78]:  party_counts  =  party_counts.loc[:,  2:5]

Luego,  normalice  para  que  cada  fila  sume  1  y  haga  la  gráfica  (vea  la  Figura  9­18):

#  Normalizar  para  sumar  1  En  
[79]:  party_pcts  =  party_counts.div(party_counts.sum(1),  axis=0)

In  [80]:  party_pcts  Out[80]:  
tamaño  día  
Vie   2 3 4 5

0.888889  0.055556  0.055556  0.000000  Sáb  0.623529  0.211765  
0.152941  0.011765
dom  0,520000  0,200000  0,240000  0,040000  jue  0,827586  
0,068966  0,086207  0,017241

En  [81]:  party_pcts.plot.bar()

Figura  9­18.  Fracción  de  fiestas  por  tamaño  en  cada  día

Entonces  puede  ver  que  el  tamaño  de  las  fiestas  parece  aumentar  el  fin  de  semana  en  este  conjunto  de  datos.

Con  datos  que  requieren  agregación  o  resumen  antes  de  hacer  un  gráfico,  usar  el  paquete  seaborn  puede  simplificar  
mucho  las  cosas.  Veamos  ahora  el  porcentaje  de  propinas  por  día  con  seaborn  (vea  la  Figura  9­19  para  el  gráfico  
resultante):

9.2  Trazado  con  pandas  y  seaborn  |  275
Machine Translated by Google

En  [83]:  importar  seaborn  como  sns

En  [84]:  propinas['tip_pct']  =  propinas['tip'] /  (propinas['total_bill']  ­  propinas['tip'])

En  [85]:  tips.head()
Out[85]:  
total_bill  propina  fumador  día tiempo  tamaño  tip_pct  No  Sun  
0 16,99  1,01 Dinner  2  0.063204
1   10,34  1,66  21,01   Cena  sin  sol 3  0.191244  3  
2 3,50 Cena  sin  sol 0.199886
3 23,68  3,31 Cena  sin  sol 2  0.162494
4 24,59  3,61 Cena  sin  sol 4  0.172069

En  [86]:  sns.barplot(x='tip_pct',  y='day',  data=tips,  orient='h')

Figura  9­19.  Porcentaje  de  propinas  por  día  con  barras  de  error

Las  funciones  gráficas  en  seaborn  toman  un  argumento  de  datos ,  que  puede  ser  un  marco  de  datos  
de  pandas.  Los  otros  argumentos  se  refieren  a  nombres  de  columnas.  Debido  a  que  hay  múltiples  
observaciones  para  cada  valor  en  el  día,  las  barras  son  el  valor  promedio  de  tip_pct.  Las  líneas  negras  
dibujadas  en  las  barras  representan  el  intervalo  de  confianza  del  95%  (esto  se  puede  configurar  a  
través  de  argumentos  opcionales).

276  |  Capítulo  9:  Trazado  y  visualización
Machine Translated by Google

seaborn.barplot  tiene  una  opción  de  tono  que  nos  permite  dividir  por  un  valor  categórico  adicional  (Figura  
9­20):

En  [88]:  sns.barplot(x='tip_pct',  y='day',  hue='time',  data=tips,  orient='h')

Figura  9­20.  Porcentaje  de  propinas  por  día  y  hora

Tenga  en  cuenta  que  seaborn  ha  cambiado  automáticamente  la  estética  de  los  gráficos:  la  paleta  de  colores  
predeterminada,  el  fondo  del  gráfico  y  los  colores  de  las  líneas  de  cuadrícula.  Puedes  cambiar  entre  
diferentes  aspectos  de  la  trama  usando  seaborn.set:

En  [90]:  sns.set(style="whitegrid")

Histogramas  y  gráficas  de  densidad  Un  

histograma  es  una  especie  de  gráfica  de  barras  que  ofrece  una  visualización  discreta  de  la  frecuencia  del  valor.
Los  puntos  de  datos  se  dividen  en  contenedores  discretos  espaciados  uniformemente,  y  se  representa  
gráficamente  el  número  de  puntos  de  datos  en  cada  contenedor.  Usando  los  datos  de  propinas  de  antes,  
podemos  hacer  un  histograma  de  los  porcentajes  de  propinas  de  la  cuenta  total  usando  el  método  plot.hist  
en  la  Serie  (vea  la  Figura  9­21):

En  [92]:  consejos['tip_pct'].plot.hist(bins=50)

9.2  Trazado  con  pandas  y  seaborn  |  277
Machine Translated by Google

Figura  9­21.  Histograma  de  porcentajes  de  propinas

Un  tipo  de  gráfico  relacionado  es  un  gráfico  de  densidad,  que  se  forma  calculando  una  estimación  de  una  
distribución  de  probabilidad  continua  que  podría  haber  generado  los  datos  observados.  El  procedimiento  
habitual  es  aproximar  esta  distribución  como  una  mezcla  de  "núcleos",  es  decir,  distribuciones  más  simples  
como  la  distribución  normal.  Por  lo  tanto,  las  gráficas  de  densidad  también  se  conocen  como  gráficas  de  
estimación  de  densidad  kernel  (KDE).  El  uso  de  plot.kde  crea  un  gráfico  de  densidad  utilizando  la  estimación  
convencional  de  mezcla  de  normales  (consulte  la  Figura  9­22):

En  [94]:  consejos['tip_pct'].plot.density()

278  |  Capítulo  9:  Trazado  y  visualización
Machine Translated by Google

Figura  9­22.  Gráfica  de  densidad  de  porcentajes  de  punta

Seaborn  hace  que  los  histogramas  y  los  gráficos  de  densidad  sean  aún  más  fáciles  a  través  de  su  método  distplot ,  
que  puede  trazar  un  histograma  y  una  estimación  de  densidad  continua  simultáneamente.  Como  ejemplo,  considere  
una  distribución  bimodal  que  consiste  en  extracciones  de  dos  distribuciones  normales  estándar  diferentes  (vea  la  figura  
9­23):

En  [96]:  comp1  =  np.random.normal(0,  1,  tamaño=200)

En  [97]:  comp2  =  np.random.normal(10,  2,  tamaño=200)

En  [98]:  valores  =  pd.Series(np.concatenate([comp1,  comp2]))

En  [99]:  sns.distplot(valores,  bins=100,  color='k')

9.2  Trazado  con  pandas  y  seaborn  |  279
Machine Translated by Google

Figura  9­23.  Histograma  normalizado  de  mezcla  normal  con  estimación  de  densidad

Gráficos  de  dispersión  o  de  puntos

Los  diagramas  de  puntos  o  de  dispersión  pueden  ser  una  forma  útil  de  examinar  la  relación  entre  dos  
series  de  datos  unidimensionales.  Por  ejemplo,  aquí  cargamos  el  conjunto  de  datos  de  macrodatos  del  
proyecto  statsmodels,  seleccionamos  algunas  variables  y  luego  calculamos  las  diferencias  de  registro:

En  [100]:  macro  =  pd.read_csv('ejemplos/macrodata.csv')

En  [101]:  datos  =  macro[['cpi',  'm1',  'tbilrate',  'unemp']]

En  [102]:  trans_data  =  np.log(data).diff().dropna()

En  [103]:  trans_datos[­5:]
Fuera[103]:
tasa  
198  ­d0.007904  
ipc  desempleado   e  billar  m01.045361  
­0.396881  0.105361
199  ­0.021979  0.066753  ­2.277267  0.139762  200  0.002340  
0.010286  0.606136  0.160343
201  0,008419  0,037461  ­0,200671  0,127339
202  0,008894  0,012202  ­0,405465  0,042560

280  |  Capítulo  9:  Trazado  y  visualización
Machine Translated by Google

Entonces  podemos  usar  el  método  regplot  de  seaborn ,  que  hace  un  diagrama  de  dispersión  y  ajusta  una  
línea  de  regresión  lineal  (vea  la  Figura  9­24):

En  [105]:  sns.regplot('m1',  'unemp',  data=trans_data)
Salida[105]:  <matplotlib.axes._subplots.AxesSubplot  en  0x7fb613720be0>

En  [106]:  plt.title('Cambios  en  el  registro  %s  frente  al  registro  %s'  %  ('m1',  'unemp'))

Figura  9­24.  Un  gráfico  de  dispersión/regresión  marino

En  el  análisis  exploratorio  de  datos,  es  útil  poder  ver  todos  los  diagramas  de  dispersión  entre  un  grupo  de  
variables;  esto  se  conoce  como  diagrama  de  pares  o  matriz  de  diagrama  de  dispersión.  Hacer  una  gráfica  
de  este  tipo  desde  cero  es  un  poco  laborioso,  por  lo  que  seaborn  tiene  una  función  de  gráfica  de  pares  
conveniente ,  que  permite  colocar  histogramas  o  estimaciones  de  densidad  de  cada  variable  a  lo  largo  de  
la  diagonal  (consulte  la  Figura  9­25  para  ver  la  gráfica  resultante):

En  [107]:  sns.pairplot(trans_data,  diag_kind='kde',  plot_kws={'alpha':  0.2})

9.2  Trazado  con  pandas  y  seaborn  |  281

También podría gustarte