summaryrefslogtreecommitdiff
path: root/src/02_datos.md
diff options
context:
space:
mode:
Diffstat (limited to 'src/02_datos.md')
-rw-r--r--src/02_datos.md662
1 files changed, 0 insertions, 662 deletions
diff --git a/src/02_datos.md b/src/02_datos.md
deleted file mode 100644
index 1eb2f1a..0000000
--- a/src/02_datos.md
+++ /dev/null
@@ -1,662 +0,0 @@
-# Trabajando con datos
-
-Programar es, principalmente, tratar con datos y los datos no son más que
-piezas de información que se estructura de una forma concreta.
-
-## Nombrando datos
-
-Los datos se almacenan en la memoria principal de la computadora. Puedes
-imaginarte la memoria como un conjunto de cajas identificadas con direcciones,
-como si fuesen los buzones de un portal con muchos pisos. Para utilizar datos,
-éstos se guardan en los diferentes cajones y se van extrayendo y actualizando.
-La importancia de nombrar las cosas es evidente, si no guardamos un listado de
-dónde hemos guardado qué no podemos recuperar los datos que hemos creado.
-
-Al igual que en las matemáticas se utilizan los símbolos para representar
-posibles valores y simplificar los procesos, en los lenguajes de programación
-se utilizan símbolos para definir referencias a los datos y así poder referirse
-a los mismos datos por su nombre sin tener que introducir su contenido
-constantemente. Los lenguajes de programación aportan al programador la
-facilidad de poder crear sus propios nombres para no tener que usar las
-direcciones propias de la memoria, que normalmente son número sin más
-significado que su posición, la mayor parte de las veces permitiendo al
-programador abstraerse de estos detalles internos.
-
-En python existe un operador para declarar símbolos que hacen referencia a un
-valor: el operador `=`. Este operador enlaza el valor de la derecha con el
-nombre de la izquierda. Con un ejemplo es más fácil de comprender. Probando en
-la REPL:
-
-``` python
->>> a = 10
->>> a
-10
->>> a + 1
-11
-```
-
-Como ves, en el primer paso se asocia el valor `10` al identificador `a` y más
-adelante se puede utilizar `a` para hacer referencia al valor enlazado.
-
-Las referencias han de ser declaradas antes de usarse, si no, el intérprete no
-las conoce y lanza una excepción:
-
-``` python
->>> b
-Traceback (most recent call last):
- File "<pyshell#6>", line 1, in <module>
- b
-NameError: name 'b' is not defined
-```
-
-> NOTA: Los nombres para poder ser interpretados correctamente por python deben
-> cumplir unas normas estrictas:
->
-> - No pueden tener espacios.
-> - Sólo pueden estar formados por combinaciones de letras, números y el
-> símbolo `_`.
-> - No pueden empezar por un dígito.
-
-### Todo es una referencia
-
-El comportamiento de estas referencias puede no ser intuitivo si vienes de
-otros lenguajes. El operador `=` enlaza, como se dice anteriormente, un nombre
-a un valor concreto, pero eso no significa que copie su contenido. En python
-los valores y los nombres son conceptos independientes. Los valores ocupan su
-lugar en la memoria y los nombres hacen referencia a dónde se encuentran estos
-valores. Es muy común tener muchas referencias a la misma estructura de datos y
-transformar su contenido desde todas las referencias.
-
-A nivel técnico, lo que en python se conoce como variable y en este documento
-hemos hecho el esfuerzo de llamar referencia es lo que en otros lenguajes se
-conoce como un puntero y la labor del operador `=` es la de asignar el puntero
-a la dirección donde se encuentran los datos a los que debe apuntar.
-
-Volviendo a la metáfora de los buzones, en lenguajes de más bajo nivel como C
-estás obligado a seleccionar qué buzón vas a utilizar para introducir cada
-dato. Por lo que si cambia el tipo de dato a gestionar, puede que el buzón se
-quede pequeño o que los datos se interpreten de forma incorrecta. Sin embargo,
-python guarda las referencias de forma independiente a los valores y adecua el
-número de buzones en uso al tamaño de los datos de los que dispones
-reordenando, si es necesario, la estructura completa de valores y referencias.
-A este concepto se le conoce como gestión automática de memoria (*automatic
-memory management*).
-
-En resumen, las referencias en python son únicamente un recordatorio que sirve
-para poder acceder a un valor. Esto cobra importancia más adelante, y no vamos
-a rehuir la responsabilidad de enfangarnos en ello.
-
-## Tipos
-
-Tal y como aclaraba el texto de la introducción, python tiene un sistema de
-tipos dinámico (*dynamic type system*). Lo que significa que gestiona los tipos
-de forma automática, permitiendo a los nombres hacer referencia a diferentes
-tipos de valor durante la ejecución del programa a diferencia de otros
-lenguajes como, por ejemplo, C, donde el tipo de las variables debe ser
-declarado de antemano y no puede cambiarse.
-
-Esto es posible debido al fenómeno explicado en el apartado anterior por un
-lado, y, por el otro, a que los datos de python son un poco más complejos que
-en otros lenguajes y guardan una pequeña nota que indica cómo deben ser
-interpretados.
-
-Si en algún momento se le pide a python que asigne un valor de un tipo distinto
-al que una referencia tenía no habrá problemas porque es el propio dato quien
-guarda la información suficiente para saber cómo entenderlo. Las referencias
-sólo almacenan dónde se guarda este dato.
-
-> NOTA: Seguramente te habrás dado cuenta de que el funcionamiento de python es
-> más ineficiente que el de C o lenguajes similares pero mucho más flexible. Y
-> así es. A la hora de elegir el lenguaje debemos valorar cuál nos interesa
-> más para la labor que vamos a realizar.
-
-### Tipos simples
-
-Hemos denominado tipos simples a los que no empaquetan más de un valor
-internamente. En otros lenguajes o contextos se les conoce como escalares.
-
-
-#### La nada
-
-La nada en python se representa con el valor `None` y es útil en innumerables
-ocasiones.
-
-#### Boolean
-
-Los valores booleanos expresan *verdad* (`True`) o *mentira* (`False`) y sirven
-para gestionar lógica desde esos términos. Mas adelante los veremos en acción.
-
-#### Integer
-
-Los Integer, números enteros en inglés, ya han aparecido anteriormente. Para
-usar un número entero puedes introducirlo tal cual. Recuerda que hay enteros
-positivos y negativos. El símbolo para marcar números negativos en python es el
-`-`, que coincide con el operador de la resta.
-
-``` python
->>> 14
-14
->>> -900
--900
->>>
-```
-
-Los números enteros también pueden expresarse en otras bases, como en
-hexadecimal. Dependiendo de la aplicación en la que te encuentres igual
-necesitas mirarlo más en detalle:
-
-``` python
->>> 0x10
-16
-```
-
-#### Float
-
-Los números Float, o de coma flotante, son números no-enteros. Ten cuidado
-con ellos porque la coma flotante es peculiar.
-
-El nombre coma flotante viene de que la coma no siempre se mantiene con la
-misma precisión. En realidad estos números se guardan como los números en
-notación científica (2,997E8 m/s para la velocidad de la luz, por ejemplo).
-La notación científica siempre implica tener una coma, pero cuya posición se
-varía con el exponente posterior.
-
-El modo de almacenamiento de los números de coma flotante es muy similar a la
-notación científica: se almacenan dos valores de tamaño fijo, la *mantisa* y el
-*exponente* para, de este modo, poder ajustar la precisión en función del
-tamaño del número almacenado. No es lo mismo expresar la velocidad de la luz,
-2,997E8 m/s, que la longitud de onda del color rojo, 6,250E-7 m, ambos
-valores tienen un tamaño muy distinto, pero usando dos *mantisas* de tamaño
-similar, cuatro dígitos (2997 y 6250), y dos exponentes de tamaño similar, un
-dígito (8 y -7), hemos expresado valores muy diferentes, ambos con la misma
-precisión relativa.
-
-El problema viene cuando nos apetece mezclarlos, por ejemplo, sumándolos.
-Imagina que tienes dos valores de esas dimensiones, uno de millones y otro de
-millonésimas partes de la unidad, y los quieres sumar entre ellos. Si sólo
-tienes una mantisa limitada para representarlos, la suma resultará en el
-redondeo de ambos a la precisión que tienes disponible. Es decir: el resultado
-será el valor grande y el pequeño se perderá en el redondeo.
-
-Aunque en este caso la suma es precisa, si tratas con un billón de valores
-pequeños y uno grande y quieres obtener la suma de todos y los sumas en parejas
-siempre con el grande, en cada suma se descartará el valor pequeño en el
-redondeo y el resultado total será el valor grande que tenías. Sin embargo, si
-sumas los valores de tamaño similar entre ellos primero, obtendrás un valor
-suficientemente grande como para alterar el resultado de la suma contra el
-valor grande al final, dando así un resultado distinto. Te recomiendo entonces,
-que si te encuentras en una situación como ésta tengas cuidado y, por ejemplo,
-ordenes los números de menor a mayor antes de sumarlos, para obtener una suma
-de buena precisión.
-
-En realidad, el exponente en el caso de python (y en casi todos los demás
-lenguajes) no está elevando un 10 a la enésima potencia, si no que lo hace con
-un 2. Por lo que lo expresado anteriormente es un poco distinto. Esto provoca
-que algunos números de coma flotante no sean tan redondos como deberían (por
-ejemplo, `2.999999999`, cuando debería ser `3.0`) y compararlos entre ellos
-puede ser desastroso. Para evitarlo, te recomiendo que *siempre* redondees los
-valores a una precisión que puedas controlar.
-
-Aunque realmente es algo más complejo[^1], lo que sabes ahora te evitará
-problemas en el futuro, sobre todo cuando analices datos, uno de los sectores
-donde python se usa de forma extensiva.
-
-[^1]: https://en.wikipedia.org/wiki/Floating-point_arithmetic
-
-Declarar números de coma flotante es natural porque usa una sintaxis a la que
-estamos acostumbrados:
-
-``` python
->>> 1E10
-10000000000.0
->>> 1.0
-1.0
->>> 0.2E10
-2000000000.0
->>>
-```
-
-
-#### Complex
-
-Python soporta números complejos y los expresa utilizando la letra `j`. Como
-suponen un caso bastante concreto no los analizaremos en detalle. Pero tienes
-disponible la documentación de python para lo que quieras.
-
-``` python
->>> 1-23j
-(1-23j)
-```
-
-#### String
-
-Un String es una cadena de caracteres. Los Strings en python son, a diferencia
-de en otros lenguajes, un escalar, al contrario de lo que la primera definición
-que hemos expresado puede hacernos pensar. En python los Strings no son un
-conjunto de caracteres alfanuméricos sueltos que se comportan como un valor, es
-al revés. El concepto de carácter no existe y ha de expresarse con un String de
-longitud 1.
-
-Los strings se expresan rodeando texto con comillas dobles, `"`, o simples `'`
-(el símbolo del apóstrofe).
-
-``` python
->>> "Hola"
-'Hola'
->>> 'Hola'
-'Hola'
-```
-
-El hecho de que haya dos opciones para delimitar los strings facilita el
-etiquetado como string de valores que contienen las propias comillas en su
-contenido. También puede utilizarse la contrabarra `\` para cancelar la acción
-de las comillas.
-
-
-``` python
->>> "Tiene un apóstrofe: Luke's"
-"Tiene un apóstrofe: Luke's"
->>> 'Tiene un apóstrofe: Luke's'
-SyntaxError: invalid syntax
->>> 'Tiene un apóstrofe: Luke\'s'
-"Tiene un apóstrofe: Luke's"
-```
-
-> NOTA: la contrabarra sirve para introducir caracteres especiales o caracteres
-> de escape[^2]: `\n` salto de línea, `\t` tabulador, etc. Que son una herencia
-> de los tiempos de las máquinas de escribir, pero son aún útiles y muy usados.
-> Para expresar la propia contrabarra ha de escaparse a sí misma con otra
-> contrabarra para que no evalúe el siguiente caracter como un caracter de
-> escape.: `\\`.
-
-[^2]: https://en.wikipedia.org/wiki/Escape_character
-
-
-### Tipos compuestos
-
-Hemos denominado tipos compuestos a los que pueden incluir diferentes
-combinaciones de tipos simples. Estos tipos dejan de ser escalares y se
-comportan como vectores o conjuntos de datos. Estos tipos de dato pueden
-contenerse a sí mismos, por lo que pueden crear estructuras anidadas complejas.
-
-#### Tuple
-
-Las tuplas o *tuple* en inglés son el tipo compuesto más sencillo en python.
-Las tuplas definen un conjunto de valores de cualquier tipo.
-
-Se declaran utilizando paréntesis añadiendo sus elementos separados por comas.
-Y se accede a sus contenidos utilizando los corchetes e introduciendo el índice
-del elemento que se quiere extraer.
-
-``` python
->>> (2, 3, "HOLA")
-(2, 3, 'HOLA')
->>> (2, 3, "HOLA")[0]
-2
-```
-En python los índices comienzan en `0`.
-
-#### List
-
-Las listas o *list* son muy similares a las tuplas, pero son algo más complejas
-porque pueden alterarse así mismas. A diferencia de todos los tipos que hemos
-visto hasta ahora, tanto las listas como los diccionarios que veremos a
-continuación son mutables. Esto significa que puede transformarse su valor, más
-adelante trataremos esto en detalle.
-
-De momento, recuerda que las listas se construyen de forma similar a las
-tuplas, pero utilizando corchetes en lugar de paréntesis. La forma de acceder a
-los índices es idéntica.
-
-``` python
->>> [2, 3, "HOLA"]
-[2, 3, 'HOLA']
->>> [2, 3, "HOLA"][0]
-2
-```
-
-#### Dictionary
-
-Los diccionarios o *dictionary* son un tipo de dato similar a los dos
-anteriores, pero que en lugar de utilizar índices basados en la posición de sus
-elementos usan claves arbitrarias definidas por el usuario.
-
-Además, los diccionarios no están ordenados así que no se puede suponer que las
-claves siempre van a estar en el orden en el que se introducen.
-
-Para declarar diccionarios es necesario indicar qué claves se quieren usar. Las
-claves son de tipo string. En el caso de los diccionarios, además, se utilizan
-llaves para definirlos. El acceso a sus valores se realiza con los corchetes,
-del mismo modo que en las listas, pero es necesario seleccionar la clave para
-acceder.
-
-Los diccionarios, al igual que las listas, son mutables. Como veremos en
-seguida.
-
-``` python
->>> {"nombre": "Guido", "apellido": "Van Rossum", "popularidad": 8.0}
-{'nombre': 'Guido', 'apellido': 'Van Rossum', 'popularidad': 8.0}
->>> {"nombre": "Guido", "apellido": "Van Rossum", "popularidad": 8.0}["popularidad"]
-8.0
-```
-
-#### Set
-
-Los *sets* son muy similares a las listas y tuplas, pero con dos
-peculiaridades:
-
-- Sus valores son únicos. No pueden repetirse.
-- No están ordenados
-
-Estas dos características tan estrictas, lejos de ser limitantes, aportan una
-mejora radical en su rendimiento. Buscar elementos en un set es extremadamente
-eficiente y se usan principalmente para esa labor.
-
-Si quieres validar en algún momento que un valor pertenece a un conjunto de
-valores, el set es el tipo de dato que estás buscando.
-
-Los sets se declaran también usando las llaves, como un diccionario, pero no
-usan claves.
-
-``` python
->>> {"a", "b", 1}
-{'a', 1, 'b'}
-```
-
-## Conversión
-
-Ahora que conoces los valores sé que quieres saber cómo cambiar de uno a otro.
-Cómo leer un Integer desde un String etc. Python tiene funciones para construir
-sus diferentes tipos de datos a partir de los diferentes inputs posibles.
-
-Aunque aún no sabes ejecutar funciones te adelanto cómo se hace con algunos
-ejemplos:
-
-``` python
->>> bool(1)
-True
->>> bool(0)
-False
->>> int("hola")
-Traceback (most recent call last):
- File "<pyshell#27>", line 1, in <module>
- int("hola")
-ValueError: invalid literal for int() with base 10: 'hola'
->>> int("10")
-10
->>> float(10)
-10.0
->>> complex(19)
-(19+0j)
->>> str(10)
-'10'
->>> tuple([1,2,3])
-(1, 2, 3)
->>> list((1,2,3))
-[1, 2, 3]
->>> dict((("a", 1),("b", 2)))
-{'a': 1, 'b': 2}
->>> set([1,2,2,3,4,4,4,4,4])
-{1, 2, 3, 4}
-```
-
-Los propios nombres de las funciones son bastante representativos de a qué tipo
-convierten. Si quieres saber más puedes ejecutar `help(nombre)` y ver qué te
-cuenta la ayuda.
-
-> NOTA: Fíjate que si conviertes una secuencia de valores repetidos a *set*
-> únicamente almacena los que no se repiten. Es uno de los usos más comunes que
-> tienen.
-
-
-## Operadores
-
-Ahora que sabes el contexto en el que vas a jugar, necesitas poder alterar los
-datos.
-
-Existen operadores básicos que te permiten transformar los datos, algunos ya
-los has visto antes, el operador `=`, que sirve para nombrar cosas, la suma
-(`+`) o la resta (`-`), pero hay otros.
-
-``` python
->>> 1 + 1
-2
->>> 10 - 9
-1
->>> 10 ** 2
-100
->>>
-```
-
-Los siguientes apartados muestran algunos operadores que es interesante
-memorizar.
-
-
-### Pruebas de verdad
-
-Las pruebas de verdad generan un valor booleano desde una pareja de valores. A
-continuación una lista de las pruebas de verdad con unos ejemplos:
-
-| operator | meaning |
-|---|---|
-| < | strictly less than |
-| <= | less than or equal |
-| > | strictly greater than |
-| >= | greater than or equal |
-| == | equal |
-| != | not equal |
-| is | object identity |
-| is not | negated object identity |
-| in | containment test |
-| not in | negated containment test |
-
-``` python
->>> 1 > 1
-False
->>> 1 >= 1
-True
->>> 1 not in (0, 2, 3)
-True
-```
-
-Aunque en otros lenguajes no es posible, la notación matemática habitual se
-puede utilizar en python concatenando pruebas de verdad:
-
-``` python
->>> x = 1.2
->>> 1 < x < 2
-True
-```
-
-### Operadores lógicos
-
-Los operadores lógicos mezclan booleanos y son muy importantes, sobre todo para
-combinar las pruebas de verdad.
-
-| operator | meaning |
-|---|---|
-| and | logical and, returns True if both True, False otherwise |
-| or | logical or, returns True if any True, False otherwise |
-| not | logical not, returns True if False, True otherwise |
-
-La mayor parte de los operadores son binarios (como la suma), necesitan dos
-valores y devuelven otro, pero existe al menos una excepción que funciona con
-un único valor. El operador `not` sirve para darle la vuelta a un Booleano.
-
-``` python
->>> not True
-False
->>> True and True
-True
->>> False and True
-False
->>> False or True
-True
->>> 1 > 0 and 2 > 1
-True
-```
-
-### Matemáticos
-
-Los operadores matemáticos transforman números entre ellos. Casi todos son
-conocidos por su operación matemática.
-
-| operator | meaning |
-|---|---|
-| + | addition |
-| - | subtraction or negative |
-| `*` | multiplication |
-| / | division |
-| `**` | power |
-| % | remainder |
-
-### Ternary operator
-
-Existe además un operador que puede usar tres parámetros, el *inline-if* (*if
-en línea*), o *ternary operator*[^ternary]. El *ternary operator* se comporta
-así:
-
-``` python
->>> 1 if 1 > 9 else 9
-9
->>> 1 if 1 < 9 else 9
-1
-```
-
-[^ternary]: <https://en.wikipedia.org/wiki/%3F:#Python>
-
-
-### Operación en función del tipo
-
-Python simplifica muchas tareas transformando el comportamiento de los
-operadores en función del tipo de dato sobre el que trabajan. Los operadores
-matemáticos están preparados para trabajar sobre números (de cualquier tipo)
-pero la verdad es que algunos pueden ejecutarse sobre otros formatos. Por
-ejemplo:
-
-``` python
->>> "a" + "a"
-'aa'
->>> [1,2] + [3,4]
-[1, 2, 3, 4]
-```
-
-Esto se debe a que la funcionalidad del operador `+` ha sido diseñada para
-operar de forma especial en Strings y en Listas, haciendo una concatenación.
-
-En el futuro, cuando aprendas a diseñar tus propios tipos podrás hacer que los
-operadores les afecten de forma especial, tal y como pasa aquí.
-
-### Precedencia
-
-La precedencia en python es muy similar a la matemática y usa las mismas reglas
-para marcarla de forma explícita. Recuerda, en matemáticas se utilizan los
-paréntesis para esto.
-
-Los operadores siempre trabajan con sus correspondientes valores y python los
-resuelve de forma ordenada. Si generas una operación muy compleja, python la
-irá desgranando paso a paso y resolviendo las parejas una a una, cuanto más te
-acostumbres a hacerlo en tu mente menos errores cometerás.
-
-``` python
->>> 8 + 7 * 10 == (8 + 7) * 10
-False
->>> 8 + 7 * 10
-78
->>> (8 + 7) * 10
-150
->>> 78 == 150
-False
-```
-
-## Mutabilidad
-
-Ya adelantamos que el operador `=` sirve para nombrar cosas. Ahora vamos a
-combinar esa propiedad con la mutabilidad, o la propiedad de las cosas de
-alterarse a sí mismas. Empecemos con un ejemplo:
-
-``` python
->>> a = 10
->>> b = a + 10
->>> b
-20
->>> a = b
->>> a
-20
-```
-
-En este ejemplo hacemos que `a` haga referencia al valor 10, y después creamos
-`b` a partir de `a` y otro 10. Gracias a la precedencia, primero se resuelve el
-lado derecho completo obteniendo un 20 y después se asigna la referencia `b` a
-ese valor.
-
-Si queremos, después podemos reasignar el símbolo `a` a otro valor nuevo, en
-este caso al que hacer referencia `b` que es 20.
-
-En este primer ejemplo, ningún valor está siendo alterado, si te fijas, sólo
-estamos creando nuevos valores y cambiando las referencias a éstos.
-
-Con los datos mutables podemos alterar los valores. Lo vemos con otro ejemplo:
-
-``` python
->>> a = {}
->>> b = a
->>> a["cambio"] = "hola!"
->>> b
-{'cambio': 'hola!'}
-```
-
-Primero creamos un diccionario vacío y le asignamos la referencia `a`. Después
-le asignamos la referencia `b` a quien referenciaba `a`, es decir, al
-diccionario vacío. Ambas referencias apuntan al mismo dato. Si después usamos
-alguna alteración en el diccionario `a` como asignarle una nueva clave, `b`,
-que hace referencia al mismo diccionario, también ve los cambios. Esto mismo
-podría hacerse si se tratara de listas, ya que tienen la capacidad de alterarse
-a sí mismas, pero nunca podría hacerse en tuplas, porque son inmutables.
-
-> NOTA: Los diccionarios y las listas soportan un montón de funciones y
-> alteraciones, no se mencionan en este apartado porque nunca terminaría. Se
-> dejan para el futuro y para los ejemplos que se verán durante el documento.
-
-Si intentásemos un ejemplo similar en tupla, no nos dejaría:
-
-``` python
->>> a = ()
->>> b = a
->>> a[0] = 1
-Traceback (most recent call last):
- File "<pyshell#67>", line 1, in <module>
- a[0] = 1
-TypeError: 'tuple' object does not support item assignment
-```
-
-Ten cuidado cuando trates con elementos mutables, sobre todo si tienen muchas
-referencias, porque puede que estés alterando los valores en lugares que no te
-interesa. Para evitar este tipo de problemas, puedes generar copias de los
-objetos, pero el proceso es poco eficiente y tedioso.
-
-En este segundo caso, creamos una copia de `a` para que `b` sea independiente
-de los cambios que ocurran en ésta. Aquí ya no estamos haciendo referencia
-desde `b` a los datos que había en `a`, sino a una copia de éstos, almacenada
-en otro lugar.
-
-``` python
->>> a = {}
->>> b = dict(a)
->>> a["cambio"] = "hola!"
->>> b
-{}
->>> a
-{'cambio': 'hola!'}
-```
-
-## Lo que has aprendido
-
-En este apartado has conocido los tipos fundamentales de python y cómo
-convertir de uno a otro. Además, al conocer los operadores y su funcionamiento
-ya eres más o menos capaz de usar python como una calculadora.
-
-Además, has tenido ocasión de entender de forma superficial varios conceptos
-avanzados como el manejo automático de memoria, el tipado dinámico y los
-números de coma flotante, que son muy interesantes a la hora de trabajar porque
-te permiten comprender la realidad que tienes a tu alrededor.