This is an excerpt from the Python Data Science Handbook by Jake VanderPlas; Jupyter notebooks are available on GitHub.

The text is released under the CC-BY-NC-ND license, and code is released under the MIT license. If you find this content useful, please consider supporting the work by buying the book!

Tipos de Datos en Python

  • Lenguajes de tipado estático como C o Java requieren que cada vriable sea declarada explicitamente
  • Python en un lenguaje de tipado dinámico y no requiere ésta especificación Por ejemplo en C especificamos una operación como sigue:
/* codigo en C */
int result = 0;
for(int i=0; i<100; i++){
    result += i;
}

En Python la operación equivalente puede escribirse de la siguiente forma:

# codigo en Python
result = 0
for i in range(100):
    result += i

La diferencia principal es

  • en C el tipo de cada variable debe especificarse explicitamente
  • en Python el tipo de cada variable es inferido dinamicamente

De este modo, en Python podemos asignar cualquier tipo de datos a cualquier variables:

# codigo Python
x = 4
x = "four"

El equivalente en C sería como sigue, sin embargo, tendría errores de compilación o consecuencias inesperadas:

/* codigo C */
int x = 4;
x = "four";  // ERROR
  • Las variables en Python son más que sólo su valor
  • Contienen información extra sobre el tipo de valor, es decir el tipo de dato que representan.

Enteros en Python

  • La implementacion standard de Python está escrita en C.
  • Si definimos un entero en Python, x = 10000, x es un apuntador a una estructura de C:
struct _longobject {
    long ob_refcnt;
    PyTypeObject *ob_type;
    size_t ob_size;
    long ob_digit[1];
};
  • Un entero en Python contiene cuatro partes:

    • ob_refcnt, un contador que ayuda en la asignación y liberación de memoria
    • ob_type, codifica el tipo de variable
    • ob_size, especifica el tamaño
    • ob_digit, contiene el valor asignado que representa la variable.
  • Esto causa un pequeño overhead cuando se crea un entero en Python en comparación con C, como se ilustra a continuación:

Integer Memory Layout

Listas en Python

Una lista es una estructura de datos que permite colectar objetos. Creamos una lista de enteros de la siguiente forma:

In [1]:
L = list(range(10))
L
Out[1]:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
In [2]:
type(L[0])
Out[2]:
int

O una lista de caracteres de texto:

In [3]:
L2 = [str(c) for c in L]
L2
Out[3]:
['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
In [4]:
type(L2[0])
Out[4]:
str

Gracias al tipado dinámico podemos crear listas heterogéneas:

In [5]:
L3 = [True, "2", 3.0, 4]
[type(item) for item in L3]
Out[5]:
[bool, str, float, int]
  • Esta felixibilidad tiene un costo pues cada elemento en la lista contiene es una referencia a una estructura que define el objeto de Python.
  • En el caso especial que todas las entradas son del mismo tipo habrá información redundante: - Sería mas eficiente usar un arreglo de tipo fijo
  • La diferencia entre listas dinámicas y de tipo fijo (como NumPy) se ilustra en la siguiente figura:

Array Memory Layout

  • El arreglo contiene un apuntador único a un bloque contiguo de datos
  • La lista contiene un apuntador a un bloque de apuntadores, cada uno apuntando a un objeto de Python (como el objeto entero que vimos antes)
  • La lista puede tener objetos y datos de cualquier tipo
  • El arreglo fijo no puede hacerlo, pero es mas eficiente para guardar y manipular los datos

Arreglos de Tipo Fijo en Python

  • En Python hay diferentes opciones para guardar datos en buffers de tipo fijo
  • El modulo interno array (disponible a partir de Python 3.3) sirve para crear arreglos densos de tipo uniforme:
In [3]:
import array
L = list(range(10))
A = array.array('i', L)
A
Out[3]:
array('i', [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
  • Aquí 'i' indica que las entradas son enteros.
  • El objeto ndarray de la libreria NumPy es mucho mas útil ya que implementa operaciones sobre los datos contenidos.
  • Antes de explorar estas operaciones, veamos diversas maneras de crear arreglos de NumPy
  • Comenzamos con la manera standard de importar NumPy, usando el alias np:
In [7]:
import numpy as np

Crear Arreglos a partir de Listas de Python

Podemos usar np.array para crear arreglos a partir de listas:

In [8]:
# integer array:
np.array([1, 4, 2, 5, 3])
Out[8]:
array([1, 4, 2, 5, 3])
  • A diferencia de las lista en Python, NumPy está restringif a arreglos que contienen el mismo tipo de datos
  • Si los tipos no coinciden NumPy trata de ajustar (upcast, emitir) si es posible
  • En el siguiente ejemplo, los enteros se emiten a punto flotante:
In [9]:
np.array([3.14, 4, 2, 3])
Out[9]:
array([ 3.14,  4.  ,  2.  ,  3.  ])
  • Podemos indicar el tipo de datos del arreglo explicitamente usando la palabra dtype:
In [10]:
np.array([1, 2, 3, 4], dtype='float32')
Out[10]:
array([ 1.,  2.,  3.,  4.], dtype=float32)
  • A diferencia de las listas, los arreglos de NumPy pueden ser explicitamente multi-dimensionales
  • A continuación, una forma de inicializar un arreglo multidimensional usando una lista de listas:
In [11]:
# nested lists result in multi-dimensional arrays
np.array([range(i, i + 3) for i in [2, 4, 6]])
Out[11]:
array([[2, 3, 4],
       [4, 5, 6],
       [6, 7, 8]])
  • Las listas interiores se toman como renglones del arreglo bidimensional resultante

Creando Arreglos

  • Para arreglos grandes, es mas eficiente crearlos usando rutinas implementadas en NumPy:
In [12]:
# Crea arreglo de enteros de longitud-10 lleno de ceros
np.zeros(10, dtype=int)
Out[12]:
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
In [13]:
# Crea arreglo con puntos flotantes de dimensiones 3x5 (3 renglones, 5 columnas) lleno de unos
np.ones((3, 5), dtype=float)
Out[13]:
array([[ 1.,  1.,  1.,  1.,  1.],
       [ 1.,  1.,  1.,  1.,  1.],
       [ 1.,  1.,  1.,  1.,  1.]])
In [14]:
# Crea arreglo de 3x5 array lleno de 3.14
np.full((3, 5), 3.14)
Out[14]:
array([[ 3.14,  3.14,  3.14,  3.14,  3.14],
       [ 3.14,  3.14,  3.14,  3.14,  3.14],
       [ 3.14,  3.14,  3.14,  3.14,  3.14]])
In [15]:
# Crea arreglo lleno con una secuencia lineal de numeros
# Empieza en 0, acaba en 20, en pasos de 2
# (similar a la funcion interna range())
np.arange(0, 20, 2)
Out[15]:
array([ 0,  2,  4,  6,  8, 10, 12, 14, 16, 18])
In [16]:
# Crea array de cinco valores igualmente espaciados entre 0 y 1
np.linspace(0, 1, 5)
Out[16]:
array([ 0.  ,  0.25,  0.5 ,  0.75,  1.  ])
In [17]:
# Crea un arreglo de 3x3 con valores aleatorios
# distribuidos de manera uniforma entre 0 y 1
np.random.random((3, 3))
Out[17]:
array([[ 0.99844933,  0.52183819,  0.22421193],
       [ 0.08007488,  0.45429293,  0.20941444],
       [ 0.14360941,  0.96910973,  0.946117  ]])
In [18]:
# Crea un arreglo de 3x3 con valores aleatorios
# distribuidos de manera normal con media 0 y desviacion estandard 1
np.random.normal(0, 1, (3, 3))
Out[18]:
array([[ 1.51772646,  0.39614948, -0.10634696],
       [ 0.25671348,  0.00732722,  0.37783601],
       [ 0.68446945,  0.15926039, -0.70744073]])
In [19]:
# Crea array de 3x3 de numeros enterros aleatorios tomados del intervalo [0, 10)
np.random.randint(0, 10, (3, 3))
Out[19]:
array([[2, 3, 4],
       [5, 7, 8],
       [0, 5, 0]])
In [20]:
# Crea matrix identidad de 3x3 (arreglo cuyas entradas son todas cero excepto las diagonales)
np.eye(3)
Out[20]:
array([[ 1.,  0.,  0.],
       [ 0.,  1.,  0.],
       [ 0.,  0.,  1.]])
In [21]:
# Crea un array de tres enteros sin valores especificos
# Los valores se toman de lo que exista en esa localizacion de la memoria
np.empty(3)
Out[21]:
array([ 1.,  1.,  1.])

Tipos de Datos Standard de NumPy

  • Los arrays de NumPy contienen valores de un solo tipo, veamos cuales pueden ser
  • NumPy está implementado en C, de modo que los tipos son similares a los usados en C, Fortran, y otros lenguajes relacionados
  • La siguiente tabla enlista los tipos de datos standard de NumPy
  • Cuando construyen un array se puede especificar el tipo usando texto:
np.zeros(10, dtype='int16')
  • O usando el objeto de NumPy asociado:
np.zeros(10, dtype=np.int16)
Data type Description
bool_ Boolean (True or False) stored as a byte
int_ Default integer type (same as C long; normally either int64 or int32)
intc Identical to C int (normally int32 or int64)
intp Integer used for indexing (same as C ssize_t; normally either int32 or int64)
int8 Byte (-128 to 127)
int16 Integer (-32768 to 32767)
int32 Integer (-2147483648 to 2147483647)
int64 Integer (-9223372036854775808 to 9223372036854775807)
uint8 Unsigned integer (0 to 255)
uint16 Unsigned integer (0 to 65535)
uint32 Unsigned integer (0 to 4294967295)
uint64 Unsigned integer (0 to 18446744073709551615)
float_ Shorthand for float64.
float16 Half precision float: sign bit, 5 bits exponent, 10 bits mantissa
float32 Single precision float: sign bit, 8 bits exponent, 23 bits mantissa
float64 Double precision float: sign bit, 11 bits exponent, 52 bits mantissa
complex_ Shorthand for complex128.
complex64 Complex number, represented by two 32-bit floats
complex128 Complex number, represented by two 64-bit floats