El Consejo Directivo de Exactas UBA aprobó la creación de la Licenciatura en Ciencias de Datos, una nueva carrera que surge de años de intenso trabajo del Instituto del Cálculo, los Departamentos de Computación, Matemática y, más recientemente, del de Física. 

¿ Por qué La Ciencia De Los Datos ? ¿Qué relación tiene con Python?

En la actualidad nos encontramos "ahogados" entre Datos diversos y abundantes; es por eso que la ciencia ligada a la computación, las matemáticas y las ciencias empresariales han comenzado a estudiarlos con el propósito de preveer comportamientos futuros. 

Python es una de las principales herramientas que se utiliza actualmente para realizar algoritmos que permitan generar conocimientos a partir del estudio de Datos 


Por su parte Python requiere de herramientas que complementan su tarea como Matplotib, NumPy, Pandas y SciPY















Profundizando....


¿Qué es Python?

Es un lenguaje de programación creado por Guido van Rossum a principios de los 90’s cuyo nombre esta inspirado en el grupo de cómicos ingleses “Monty Python”.

Es un lenguaje interpretado o de script ya que se ejecuta utilizando un intérprete en lugar de compilar el código.

  • Características de Python

Tipado dinámico: El tipo de dato de una variable se determina según el valor asignado.

Fuertemente tipado: No se pueden tratar a variables como otro tipo distinto al  definido originalmente.

Multiplataforma: Python puede ejecutarse en UNIX, Solaris, Linux, DOS, Windows, OS/2, Mac OS, etc.

Conceptos básicos de programación que implementa Python

Tipos básicos: Numéricos

Enteros: Número positivo o negativo y se expresan con el tipo de datos int

 Al asignar un número a una variable esta pasará a tener tipo int, a menos que el número sea tan grande como para requerir el uso del tipo long.

# type(entero) devolvería int

entero = 23

También podemos indicar a Python que un número se almacene usando long añadiendo una L al final:

# type(entero) devolvería long

entero = 23L 

Reales: Número positivo o negativo con decimales y se expresan con el tipo float

 Para representar un número real en Python se escribe primero la parte entera, seguido de un punto y por último la parte decimal.

real = 0.2703


Complejos: Los números complejos son aquellos que tienen parte imaginaria.

Los números complejos en Python se representan de la siguiente forma:

complejo = 2.1 + 7.8j


  • Operadores aritméticos

Suma                                    +             r = 3 + 2      # r es 5

Resta                                    -              r = 4 - 7       # r es -3

Multiplicación                        *              r = -7           # r es -7

Exponente                            **             r = 2 ** 6       # r es 64

División                                /               r = 3.5 / 2      # r es 1.75

         División entera                     //              r = 3.5 // 2     # r es 1.0

         Módulo                                 %             r = 7 % 2       # r es 1

  • Operadores booleanos

and &          r = 3 & 2       # r es 2

or |            r = 3 | 2        # r es 3

xor ^           r = 3 ^ 2       # r es 1

not ~           r = ~3          # r es -4

Desplazamiento a la derecha >>        r = 3 << 1       # r es 6

Desplazamiento a la izquierda<<        r = 3 >> 1       # r es 1

  • Cadenas

Las cadenas de texto o string puden ser representadas encerrando el contenido dentro de comillas simples (‘cadena’) o dobles (“cadena”). Dentro de las comillas se pueden añadir caracteres especiales escapándolos con \, como \n, el carácter de nueva línea, o \t, el de tabulación.

Las cadenas también admiten operadores como +, que funciona realizando una concatenación de las cadenas utilizadas como operandos y *, en la que se repite la cadena tantas veces como lo indique el número utilizado como segundo operando. 

a = “uno” 
b = “dos” 
c = a + b   # c es “unodos” 
c = a * 3    # c es “unounouno”
 
  • Prefijos
u: Se utiliza al definir de forma explicita una cadena Unicode
r: Para definir cadenas raw

  • Tipos de datos Booleanos
El tipo de dato bool es una subclase del tipo int que solo pude tener dos valores True o False

  • Y sus operadores lógicos o condicionales

 and: Es verdadero cuando todos los valores son True    r = True and False   # r es False

 or: Es verdadero cuando alguno de los valores es True    r = True or False     # r es True

 not: Niega el valor de booleano   r = not True       # r es False

 
    Operador             Descripción                                  Ejemplo

==               ¿son iguales a y b?           r = 5 == 3     # r es False

!=                 ¿son distintos a y b?        r = 5 != 3       # r es True 

<                  ¿es a menor que b?         r = 5 < 3        # r es False 

 >                ¿es a mayor que b?           r = 5 > 3       # r es True

<=              ¿es a menor o igual que b? r = 5 <= 5    # r es True 

 >=             ¿es a mayor o igual que b? r = 5 >= 3     # r es True

 

Colecciones (listas, tuplas y diccionarios)

Listas 

 Serían el  equivalente a lo que en otros lenguajes se conoce por arrays, o vectores. La lista es un tipo de colección ordenada que pude contener números, cadenas, booleanos u otras listas.

 Se puede definir una lista indicando los valores entre corchetes separados por comas

l = [22, True, “una lista”, [1, 2]] 

el índice del primer elemento de la lista es 0, y no 1: 

l = [11, False] mi_var = l[0] # mi_var vale 11 

 Se accede a los valores de una lista por medio del índice entre corchetes.

l = [“una lista”, [1, 2]] 

 mi_var = l[1][0] # mi_var vale 1

También podemos utilizar este operador para modificar un elemento de la lista si lo colocamos en la parte izquierda de una asignación:

l = [22, True]

l[0] = 99 # Con esto l valdrá [99, True]

  • Índices negativos  

 Una curiosidad sobre el operador [ ] de Python es que podemos utilizar también números negativos. Si se utiliza un número negativo como índice, esto se traduce en que el índice empieza a contar desde el final, hacia la izquierda; es decir, con [-1] accederíamos al último elemento de la lista, con [-2] al penúltimo, con [-3], al antepenúltimo, y así sucesivamente

  • El slicing o particionado permite obtener subconjuntos de una lista utilizando la siguiente notación dentro de los corchetes:  
  • [inicio:fin]: para obtener un sub conjunto desde la inicio(inclusivo) hasta el fin(no inclusivo)
  • [inicio:fin:salto]: para obtener un sub conjunto desde la inicio(inclusivo) hasta el fin(no inclusivo), el salto se utiliza para tomar valores de la lista evitando los que se encentren en medio del salto. 

 l = [99, True, “una lista”, [1, 2]] 

mi_var = l[0:2] # mi_var vale [99, True]

mi_var = l[0:4:2] # mi_var vale [99, “una lista”]

  • [:fin]: Se pueden obtener los primeros valores de una lista omitiendo el índice 0 esta notación es igual a [0:fin]
  • [incio:]: De igual forma se pueden obtener los últimos valores de una lista omitiendo el final de la misma. 
  • Operaciones útiles con las listas. 
  • [::2]: Posiciones pares de la lista   
  • [1::2]: Posiciones impares de la lista 
  • [::-1]: Lista inversa

 Hay que mencionar así mismo que no es necesario indicar el principio y el final del slicing, sino que, si estos se omiten, se usarán por defecto las posiciones de inicio y fin de la lista, respectivamente:

l = [99, True, “una lista”]

mi_var = l[1:] # mi_var vale [True, “una lista”]

 mi_var = l[:2] # mi_var vale [99, True]

mi_var = l[:] # mi_var vale [99, True, “una lista”]

mi_var = l[::2] # mi_var vale [99, “una lista”]

También podemos utilizar este mecanismo para modificar la lista:

l = [99, True, “una lista”, [1, 2]]

l[0:2] = [0, 1] # l vale [0, 1, “una lista”, [1, 2]]

pudiendo incluso modificar el tamaño de la lista si la lista de la parte derecha de la asignación tiene un tamaño menor o mayor que el de la selección de la parte izquierda de la asignación:

l[0:2] = [False] # l vale [False, “una lista”, [1, 2]]

En todo caso las listas ofrecen mecanismos más cómodos para ser modificadas a través de las funciones de la clase correspondiente, aunque no veremos estos mecanismos hasta más adelante, después de explicar lo que son las clases, los objetos y las funciones.

    • Tuplas

Las tuplas se definen con separando únicamente los valores con comas, se puede utilizar el paréntesis en lugar de corchete para dar claridad a la definición de las tuplas.

t = (1, 2, True, “python”)

 Para referirnos a elementos de una tupla, como en una lista, se usa el operador []:

mi_var = t[0] # mi_var es 1

mi_var = t[0:2] # mi_var es (1, 2)

    • Diccionarios
Los diccionarios o matrices asociativas son colecciones que relacionan una clave con un valor, los diccionarios no tienen un orden y se accede a los valores por por medio de la clave entre corchetes.

 Los diccionarios, también llamados matrices asociativas, deben su nombre a que son colecciones que relacionan una clave y un valor. Por ejemplo, veamos un diccionario de películas y directores:

d = {“Love Actually “: “Richard Curtis”,

 “Kill Bill”: “Tarantino”,

“Amélie”: “Jean-Pierre Jeunet”}

 

El primer valor se trata de la clave y el segundo del valor asociado a la clave. Como clave podemos utilizar cualquier valor inmutable: podríamos usar números, cadenas, booleanos, tuplas, … pero no listas o diccionarios, dado que son mutables. Esto es así porque los diccionarios se implementan como tablas hash, y a la hora de introducir un nuevo par clave-valor en el diccionario se calcula el hash de la clave para después poder encontrar la entrada correspondiente rápidamente. 
Si se modificara el objeto clave después de haber sido introducido en el diccionario, evidentemente, su hash también cambiaría y no podría ser encontrado.
La diferencia principal entre los diccionarios y las listas o las tuplas es que a los valores almacenados en un diccionario se les accede no por su índice, porque de hecho no tienen orden, sino por su clave, utilizando de nuevo el operador [].

 

d[“Love Actually “]   # devuelve “Richard Curtis”

 

Al igual que en listas y tuplas también se puede utilizar este operador para reasignar valores.

 

d[“Kill Bill”] = “Quentin Tarantino”

 

Sin embargo en este caso no se puede utilizar slicing, entre otras cosas porque los diccionarios no son secuencias, si no mappings (mapeados, asociaciones).


    • Sentencias condicionales

La forma más simple de una sentencia condicional es un if que evaluará una operación relacional para ejecutar un código en caso de que el resultado se True.  Cuando la condición no se debemos usar la sentencia condicional else. Pueden existir más comparaciones para una sentencia condicional para eso podemos ultilizar el if, que es una contracción de else if.

 fav = “laboratorio37”

# si (if) fav es igual a “laboratorio37”

if fav == “laboratorio37”:

 print “Tienes buen gusto!”

 print “Gracias”

 else:

 print “Vaya, que pena"

    • Bucles: while

El bucle o ciclo while ejecuta un framento de código mientras el el resultado de una operación relacional se cumpla.

 edad = 0

while edad < 18:

 edad = edad + 1

 print “Felicidades, tienes “ + str(edad)

 La variable edad comienza valiendo 0. Como la condición de que edad es menor que 18 es cierta (0 es menor que 18), se entra en el bucle. 

Se aumenta edad en 1 y se imprime el mensaje informando de que el usuario ha cumplido un año. Recordad que el operador + para las cadenas funciona concatenando ambas cadenas. Es necesario utilizar la función str (de string, cadena) para crear una cadena a partir del número, dado que no podemos concatenar números y cadenas, pero ya comentaremos esto y mucho más en próximos capítulos.

Ahora se vuelve a evaluar la condición, y 1 sigue siendo menor que 18, por lo que se vuelve a ejecutar el código que aumenta la edad en un año e imprime la edad en la pantalla. El bucle continuará ejecutándose hasta que edad sea igual a 18, momento en el cual la condición dejará de cumplirse y el programa continuaría ejecutando las instrucciones siguientes al bucle.

    • Bucles: for … in
En Python el bucle for se utiliza para facilitar la iteración de elementos de una secuencia. 

 

secuencia = [“uno”, “dos”, “tres”]
for elemento in secuencia:
 print elemento

 

Funciones

Una función es un fragmento de código, que encapsula una serie de tareas y devuelve un valor, como tal en Python no existen los procedimiento ya que la función regresará un valor Nulo en caso de no especificar un valor de retorno.

 En Python las funciones se declaran de la siguiente forma:

def mi_funcion(param1, param2):

 print param1

 print param

 

Orientación a objetos

Python es en leguaje multiparadigma, por lo tanto se puede trabajar con programación estructurada, funcional u orientada a objetos.

En realidad en Python todo es un objeto por lo que el paradigma principal es el orientado a objetos, donde la abstracción de los conceptos de modela a través de clases y objetos, y la interacción de los programas se realiza a través de estos objetos.

En Python las clases de definen por medio de la palabra reservada class, seguido del nombre de la clase y dos puntos (:)

  • Creación del objeto

 En Python las clases se definen mediante la palabra clave class seguida del nombre de la clase, dos puntos (:) y a continuación, indentado, el cuerpo de la clase. Como en el caso de las funciones, si la primera línea del cuerpo se trata de una cadena de texto, esta será la cadena de documentación de la clase o docstring.

class Coche:

 “””Abstraccion de los objetos coche.”””

 def __init__(self, gasolina):

 self.gasolina = gasolina

 print “Tenemos”, gasolina, “litros”

 def arrancar(self):

 if self.gasolina > 0:

 print “Arranca”

 else:

 print “No arranca”

 def conducir(self):

 if self.gasolina > 0:

 self.gasolina -= 1

 print “Quedan”, self.gasolina, “litros”

 else:

 print “No se mueve”


 Para crear un objeto se escribiría el nombre de la clase seguido de cualquier parámetro que sea necesario entre paréntesis. Estos parámetros son los que se pasarán al método __init__, que como decíamos es el método que se llama al instanciar la clase.

mi_coche = Coche(3)

 Ahora que ya hemos creado nuestro objeto, podemos acceder a sus atributos y métodos mediante la sintaxis objeto.atributo y objeto.

metodo():

>>> print mi_coche.gasolina

3

>>> mi_coche.arrancar()

Arranca

>>> mi_coche.conducir()

Quedan 2 litros

>>> mi_coche.conducir()

Quedan 1 litros

>>> mi_coche.conducir()

Quedan 0 litros

>>> mi_coche.conducir()

 No se mueve

>>> mi_coche.arrancar()

No arranca

>>> print mi_coche.gasolina

0

 

  • Herencia : Hay tres conceptos que son básicos para cualquier lenguaje de programación orientado a objetos: el encapsulamiento, la herencia y el polimorfismo.
En un lenguaje orientado a objetos cuando hacemos que una clase (subclase) herede de otra clase (superclase) estamos haciendo que la subclase contenga todos los atributos y métodos que tenía la superclase. No obstante al acto de heredar de una clase también se le llama a menudo “extender una clase”. 

 Para indicar que una clase hereda de otra se coloca el nombre de la clase de la que se hereda entre paréntesis después del nombre de la clase:

class Instrumento:

 def __init__(self, precio):

self.precio = precio 

 def tocar(self): 

 print “Estamos tocando musica”

 def romper(self):

 print “Eso lo pagas tu”

 print “Son”, self.precio, “$$$”

class Bateria(Instrumento):
 pass

class Guitarra(Instrumento):
 pass

 

Como Bateria y Guitarra heredan de Instrumento, ambos tienen un método tocar() y un método romper(), y se inicializan pasando un parámetro precio. Pero, ¿qué ocurriría si quisiéramos especificar un nuevo parámetro tipo_cuerda a la hora de crear un objeto Guitarra? 
Bastaría con escribir un nuevo método __init__ para la clase Guitarra que se ejecutaría en lugar del __init__ de Instrumento. Esto es lo que se conoce como sobreescribir métodos

  •  Herencia múltiple

Python permite la herencia de más de una superclase, en caso de existir métodos con el mismo nombre esto se reescribirán, teniendo mayor importancia las superclase de la izquierda. Una clase puede heredar de varias clases a la vez. Por ejemplo, podríamos tener una clase Cocodrilo que heredara de la clase Terrestre, con métodos como caminar() y atributos como velocidad_caminar y de la clase Acuatico, con métodos como nadar() y atributos como velocidad_nadar. Basta con enumerar las clases de las que se hereda separándolas por comas:

class Cocodrilo(Terrestre, Acuatico):

 pass

En el caso de que alguna de las clases padre tuvieran métodos con el mismo nombre y número de parámetros las clases sobreescribirían la implementación de los métodos de las clases más a su derecha en la definición.

En el siguiente ejemplo, como Terrestre se encuentra más a la izquierda, sería la definición de desplazar de esta clase la que prevalecería, y por lo tanto si llamamos al método desplazar de un objeto de tipo Cocodrilo lo que se imprimiría sería “El animal anda”.

class Terrestre:

 def desplazar(self):

 print “El animal anda”

class Acuatico:

 def desplazar(self):

 print “El animal nada”

class Cocodrilo(Terrestre, Acuatico):

 pass

c = Cocodrilo()

c.desplazar()


  • Polimorfismo

En Python el polimorfismo se lleva a cabo a través de la herencia, dónde el comportamiento puede cambiar al reescribir un método de la superclase en la definición de la subclase, en ocasiones se utiliza al termino polimorfismo a la sobrecarga de métodos, Pyhon al ser un lenguaje de tipado dinámico no impone restricciones a los parámetros que se pasan a una función, por lo que este tipo de comportamiento no es relevante.

  • Encapsulamiento
La encapsulación en los lenguajes de programación Orientada a Objetos es la capacidad de restringir el acceso a los métodos y atributos de la clase, en leguajes de programación como Java se determina la visibilidad por medio anteponiendo public o private, para el caso de Python todos los elementos son públicos y para definir un elemento privado el nombre del atributo o método debe iniciar con dos guiones bajos (__atributo)

 En el siguiente ejemplo sólo se imprimirá la cadena correspondiente al método publico(), mientras que al intentar llamar al método __privado() Python lanzará una excepción quejándose de que no existe (evidentemente existe, pero no lo podemos ver porque es privado).

class Ejemplo: 

 def publico(self):

 print “Publico”

 def __privado(self):

 print “Privado”

ej = Ejemplo()

ej.publico()

ej.__privado()

 

Este mecanismo se basa en que los nombres que comienzan con un doble guión bajo se renombran para incluir el nombre de la clase (característica que se conoce con el nombre de name mangling). Esto implica que el método o atributo no es realmente privado, y podemos acceder a él mediante una pequeña trampa:

ej._Ejemplo__privado()
En ocasiones también puede suceder que queramos permitir el acceso a algún atributo de nuestro objeto, pero que este se produzca de forma controlada. Para esto podemos escribir métodos cuyo único cometido sea este, métodos que normalmente, por convención, tienen nombres como getVariable y setVariable; de ahí que se conozcan también con el nombre de getters y setters.

 

class Fecha():

 def __init__(self):

 self.__dia = 1

 def getDia(self):

 return self.__dia

 def setDia(self, dia):

 if dia > 0 and dia < 31:

 self.__dia = dia

 else:

 print “Error”

mi_fecha = Fecha()

mi_fecha.setDia(33)

 

Esto se podría simplificar mediante propiedades, que abstraen al usuario del hecho de que se está utilizando métodos entre bambalinas para obtener y modificar los valores del atributo: 

 

class Fecha(object):

 def __init__(self): 

 self.__dia = 1 

 def getDia(self): 

 return self.__dia 

 def setDia(self, dia): 

 if dia > 0 and dia < 31: 

 self.__dia = dia 

 else: 

 print “Error” 

 dia = property(getDia, setDia) 

mi_fecha = Fecha() 

mi_fecha.dia = 33

 


0 comments:

Post a Comment