# Notebook from magister-informatica-uach/INFO267
Path: unidad1/4_red_convolucional.ipynb

%%HTML

_____no_output_____%autosave 0
%matplotlib notebook
import numpy as np
import matplotlib.pyplot as plt
from IPython.display import display
import ipywidgets as widgets
from matplotlib import animation
from functools import partial
slider_layout = widgets.Layout(width='600px', height='20px')
slider_style = {'description_width': 'initial'}
IntSlider_nice = partial(widgets.IntSlider, style=slider_style, layout=slider_layout, continuous_update=False)
FloatSlider_nice = partial(widgets.FloatSlider, style=slider_style, layout=slider_layout, continuous_update=False)
SelSlider_nice = partial(widgets.SelectionSlider, style=slider_style, layout=slider_layout, continuous_update=False)_____no_output_____

# Procesamiento digital de imágenes

Una imagen es una colección de pixeles ordenados

En estándar RGB cada pixel corresponde a 3 valores enteros de 8 bit (256 niveles). Combinándolos formamos colores (aproximadamente 16.7M)

Otra codificación usual para los pixeles consiste en usar un número entre cero y uno para cada canal (color)

El estándar RGBA añade un canal que representa la opacidad

Las imágenes en escala de grises y sin opacidad se pueden representar usando un canal_____no_output_____

img = plt.imread('cameraman.png')
img_bw = 0.2989*img[:, :, 0] + 0.587*img[:, :, 1]+ 0.114*img[:, :, 2]

display(img_bw.shape)
display(img_bw.dtype)

plt.figure(figsize=(4, 4))
plt.imshow(img_bw, cmap=plt.cm.Greys_r);_____no_output_____

Para nuestros sistemas digitales una imagen es una arreglo multidimensional y podemos operarlo como tal

¿A qué corresponde este segmento del arreglo?_____no_output_____

subimg = np.copy(img_bw[50:100, 120:180])
display(subimg)_____no_output_____plt.figure(figsize=(3, 3))
plt.imshow(subimg, cmap=plt.cm.Greys_r);_____no_output_____

¿Y este segmento?_____no_output_____

fig, ax = plt.subplots(2, 1, figsize=(6, 3))
ax[1].plot(subimg[30, :])
ax[0].imshow(subimg[30:31, :], cmap=plt.cm.Greys_r);_____no_output_____

## Convolución  y correlación cruzada discreta

Una herramienta clásica de procesamiento digital de señales es la **convolución**

La operación de convolución entre dos señales unidimensionales discretas es

$$
(f * g) [n] = \sum_{m=-\infty}^\infty  f[m] g[n-m]
$$

y la operación de correlación cruzada es

$$
(f \star g) [n] = \sum_{m=-\infty}^\infty  f[m] g[m+n]
$$

> Para ambas operaciones el resultado es una nueva señal que también depende de  $n$

Por ejemplo el elemento $0$ de $f\star g$ se calcula como

    f[0] g[0] + f[1] g[1] + f[2] g[2] + ...

Luego el elemento $1$ sería

    f[0] g[1] + f[1] g[2] + f[2] g[3] + ...

¿Cómo se ve esta operación graficamente?_____no_output_____

import scipy.signal
plt.close('all'); fig, ax = plt.subplots(2, figsize=(7, 4))
ax2 = ax[0].twinx()
data = subimg[0, :]

def filt(k):
    kernel = np.zeros(shape=(len(data),))
    kernel[k:k+5] = 1
    #kernel[k] = 1.; kernel[k+1] = -1.
    return kernel

true_filt = filt(0)[np.absolute(filt(0)) >0]
display(true_filt)
conv_s = scipy.signal.correlate(data, true_filt, mode='valid')

def update(k):
    ax[0].cla(); ax[1].cla(); ax2.cla();
    ax[0].plot(data)
    ax2.plot(filt(k), c='r')
    ax[1].plot(conv_s);
    ax[1].scatter(k, conv_s[k], s=100, c='k')

anim = animation.FuncAnimation(fig, update, frames=len(conv_s), interval=200, blit=True)_____no_output_____

## Filtrado de imágenes con convoluciones

Se puede extender el concepto de convolución a dos dimensiones
$$
(I_1 * I_2) [n_1, n_2] = \sum_{m_1=-\infty}^\infty \sum_{m_2=-\infty}^\infty I_1[m_1, m_2] I_2[n_1-m_2, n_2 - m_2]
$$

donde $n_1$ es el índice de las filas y $n_2$ es el índice de las columnas

#### La convolución entre dos imágenes es una nueva imagen

La imagen $I_1$ es la entrada

La imagen $I_2$ se denomina filtro o kernel de la convolución

La imagen resultante es la imagen filtrada_____no_output_____#### Filtro pasa-bajo

Suaviza, elimina los detalles_____no_output_____

D = 3
filt = np.zeros(shape=(D, D))
filt[1:-1, 1:-1] = 1
display(filt)
img_res = scipy.signal.correlate2d(subimg, filt/np.sum(filt), mode='valid')

fig, ax = plt.subplots(1, 3, figsize=(8, 3), tight_layout=True)
ax[0].imshow(subimg, cmap=plt.cm.Greys_r)
ax[1].imshow(filt, cmap=plt.cm.Greys_r)
ax[2].imshow(img_res, cmap=plt.cm.Greys_r);_____no_output_____

#### Filtro pasa-alto

Resalta los cambios bruscos, elimina las partes "planas"_____no_output_____

filt = np.array([[1., -1.]]*2)
display(filt)
img_res = scipy.signal.correlate2d(subimg, filt, mode='valid')

fig, ax = plt.subplots(1, 3, figsize=(8, 3), tight_layout=True)
ax[0].imshow(subimg, cmap=plt.cm.Greys_r)
ax[1].imshow(filt, cmap=plt.cm.Greys_r)
ax[2].imshow(img_res, cmap=plt.cm.Greys_r);_____no_output_____

#### Detector de patillas

¿Detecta patillas de fotografos mirando al horizonte?_____no_output_____

filt = np.ones(shape=(11, 11))
filt[:9, 2:9] = 0
display(filt)_____no_output_____img_res = scipy.signal.correlate2d(subimg, filt-np.mean(filt), mode='valid')

fig, ax = plt.subplots(1, 3, figsize=(8, 3), tight_layout=True)
ax[0].imshow(subimg, cmap=plt.cm.Greys_r)
maxloc = np.unravel_index(np.argmax(img_res), shape=img_res.shape)
ax[0].scatter(maxloc[1]+filt.shape[0]//2, maxloc[0]+filt.shape[1]//2, c='r', s=20)
ax[1].imshow(filt, cmap=plt.cm.Greys_r)
ax[2].imshow(img_res, cmap=plt.cm.Reds);_____no_output_____

En realidad el filtro se activa con cualquier cosa con forma de "U"

El punto es:

> Podríamos aprender filtros para detectar objetos específicos

En ese caso:

> Necesitamos aprender los valores de los "píxeles" del kernel_____no_output_____# Visión computacional

La [visión computacional](http://szeliski.org/Book/) es un campo de investigación que busca que los computadores sean capaces de "comprender" el contenido presente en imágenes digitales y video

#### Objetivo:

> Automatizar tareas realizas por el sistema visual humano

- Clasificación y Reconocimiento: ¿A qué categoría corresponde el patrón en la imagen?
- Detección, Localización y Segmentación: ¿Dónde está el patrón en la imagen?
- [Estimación de pose](https://modelzoo.co/blog/deep-learning-models-and-code-for-pose-estimation)
- [Reconstrucción](https://www.youtube.com/watch?v=gg0F5JjKmhA), [super-resolución](https://www.extremetech.com/extreme/132950-csi-style-super-resolution-image-enlargment-yeeaaaah) y [síntesis](https://tcwang0509.github.io/pix2pixHD/)
- ...

#### Aplicaciones

- [Medicina](https://www.rsipvision.com/medical-segmentation/)
- [Navegación autónoma](https://www.youtube.com/watch?v=H7Ym3DMSGms)
- [Sistemas de control de tráfico](https://www.youtube.com/watch?v=jxhAWuImxS8)
- [Realidad aumentada](https://www.youtube.com/watch?v=r9hVypi_6TQ)
- [Agricultura y forestal](https://medium.com/@awangenh/mapping-weeds-and-crops-in-precision-agriculture-with-convolutional-neural-networks-138dab87ba00)
- [...](https://www.cs.ubc.ca/~lowe/vision.html)

#### Herramientas

- Procesamiento digital de imágenes
- Optimización, Estadística
- Machine learning y en particular  **Redes Neuronales Convolucionales**

#### Desafios

- Algoritmos invariantes a los cambios de Iluminación
- Algoritmos invariantes a los cambios de escala y perspectiva (deformación)
- Algoritmos robustos contra la oclusión_____no_output_____# Redes Neuronales Convolucionales

[Slides 52-92](https://docs.google.com/presentation/d/1IJ2n8X4w8pvzNLmpJB-ms6-GDHWthfsJTFuyUqHfXg8/edit#slide=id.g3a1a71fe7e_8_192)_____no_output_____# Red Neuronal Convolucional en PyTorch

Las redes neuronales convolucionales utilizan principalmente tres tipos de capas

#### [Capas convolucionales](https://pytorch.org/docs/stable/nn.html#convolution-layers)

- Las neuronas de estas capas se organizan en filtros
- Se realiza la correlación cruzada entre la imagen de entrada y los filtros
- Existen capas convolucionales 1D, 2D y 3D

[Visualización de convoluciones con distintos tamaños, strides, paddings, dilations](https://github.com/vdumoulin/conv_arithmetic)

Los parámetros disponibles son_____no_output_____

torch.nn.Conv2d(in_channels, #Cantidad de canales de la imagen de entrada
                out_channels, #Cantidad de bancos de filtro
                kernel_size, #Tamaño de los filtros (entero o tupla)
                stride=1, #Paso de los filtros
                padding=0, #Cantidad de filas y columnas para agregar a la entrada antes de filtrar
                dilation=1, #Espacio entre los pixeles de los filtros
                groups=1, #Configuración cruzada entre filtros de entrada y salida
                bias=True,  #Utilizar sesgo (b)
                padding_mode='zeros' #Especifica como agregar nuevas filas/columnas (ver padding)
                )_____no_output_____

#### [Capas de pooling](https://pytorch.org/docs/stable/nn.html#pooling-layers)

- Capa que reduce la dimensión (tamaño) de su entrada
- Se usa tipicamente luego de una capa de convolución "activada"
- Realiza una operación no entrenable:
    - Promedio de los píxeles en una región (kernel_size=2, stride=2)

            1 2 1 0
            2 3 1 2      2.00 1.00
            0 1 0 1      0.75 0.25
            2 0 0 0

    - Máximo de los pixeles en una región (kernel_size=2, stride=2)

            1 2 1 0
            2 3 1 2      3 2
            0 1 0 1      2 1
            2 0 0 0
- Estas capas ayudan a reducir la complejidad del modelo
- También otorgan "invarianza local a la traslación", es decir que la posición donde estaba el patrón es menos relevante luego de aplicar pooling

Los parámetros de MaxPooling para entradas de dos dimensiones en torch son:_____no_output_____

torch.nn.MaxPool2d(kernel_size, # Mismo significado que en Conv2d
                   stride=None, # Mismo significado que en Conv2d
                   padding=0, #Mismo significado que en Conv2d
                   dilation=1, #Mismo significado que en Conv2d
                   return_indices=False, #Solo necesario para hacer unpooling
                   ceil_mode=False #Usar ceil en lugar de floor para calcular el tamaño de la salida
                  )_____no_output_____

#### [Capas completamente conectadas](https://pytorch.org/docs/stable/nn.html#torch.nn.Linear)

- Idénticas a las usadas en redes tipo MLP
- Realizan la operación: $Z = WX + b$        _____no_output_____

torch.nn.Linear(in_features, #Neuronas en la entrada
                out_features,  #Neuronas en la salida
                bias=True  #Utilizar sesgo (b)
                )_____no_output_____

## Mi primera red convolucional para clasificar en pytorch

Clasificaremos la base de datos MNIST (10 clases)

La arquitectura inicial considera
- Dos capas convolucionales con 8 bancos de filtros
- La primera capa convolucional espera un minibatch de imágenes de 1 canal (blanco y negro)
- Las capas convolucionales usan filtros de 3x3 píxeles
- Se considera la función de activación [Rectified Linear Unit (ReLU)](https://pytorch.org/docs/stable/nn.html#relu)
- Se usa max-pooling de tamaño 2x2 y stride 2
- Se usan dos capas completamente conectadas

**Importante** La función `reshape` convierte el tensor de 4 dimensiones a uno de 2 dimensiones. Esto prepara un tensor que sale de una capa convolucional (o pooling) para ingresarlo a las capas completamente conectadas_____no_output_____

import torch
print(torch.__version__)

class mi_red_convolucional(torch.nn.Module):

    def __init__(self, n_hidden=20, n_filters=8):
        super(mi_red_convolucional, self).__init__()
        # Extracción de características
        self.conv1 = torch.nn.Conv2d(in_channels=1, out_channels=n_filters, kernel_size=3)
        self.conv2 = torch.nn.Conv2d(in_channels=n_filters, out_channels=8, kernel_size=3)
        self.mpool2x2 = torch.nn.MaxPool2d(kernel_size=2, stride=2)
        # Clasificación
        self.fc1 = torch.nn.Linear(in_features=8*5*5, out_features=n_hidden)
        self.fc2 = torch.nn.Linear(in_features=n_hidden, out_features=10)
        self.activation = torch.nn.ReLU()

    def forward(self, x):
        z = self.mpool2x2(self.activation(self.conv1(x)))
        z = self.mpool2x2(self.activation(self.conv2(z)))
        # print(z.shape)
        z = z.reshape(-1, 8*5*5)
        z = self.activation(self.fc1(z))
        return self.fc2(z)

model = mi_red_convolucional()
# model.forward(mnist_train_data[0][0].unsqueeze(0))
display(model)_____no_output_____

### Funciones de activación en PyTorch

En la red neuronal que acabamos de implementar abandonamos la función de activación sigmoide por la [Rectified Linear Unit (ReLU)](https://pytorch.org/docs/stable/nn.html#relu)

Problema de las funciones de activación clásicas (sigmoide y tangente hiperbólica):
> Extensas zonas saturadas (planas): Cero gradiente

Con esto en mente se diseñó la función
$$
\text{ReLU}(x) = \max(0, x),
$$
que solo satura en los números negativos

> En la práctica usar ReLU acelera considerablemente el entrenamiento: más fácil de calcular y menos "muertes" de gradiente

**Atención**

Esta es una área de investigación activa

Existen muchas funciones de activación que se han propuesto posterior a ReLU

Recomendación: Partir con ReLU y luego probar [otras](https://pytorch.org/docs/stable/nn.html#non-linear-activations-weighted-sum-nonlinearity) (LeakyReLU, ELU)_____no_output_____

x = torch.linspace(-5, 5)
fig, ax = plt.subplots(2, 2, figsize=(6, 4), tight_layout=True)

activation = torch.nn.Sigmoid()
y = activation(x)
ax[0, 0].plot(x.numpy(), y.numpy());
ax[0, 0].set_title('Sigmoide')

activation = torch.nn.Tanh()
y = activation(x)
ax[1, 0].plot(x.numpy(), y.numpy());
ax[1, 0].set_title('Tangente Hiperbólica')

activation = torch.nn.ReLU()
y = activation(x)
ax[0, 1].plot(x.numpy(), y.numpy());
ax[0, 1].set_title('ReLU')

activation = torch.nn.ELU()
y = activation(x)
ax[1, 1].plot(x.numpy(), y.numpy());
ax[1, 1].set_title('ELU');_____no_output_____

### Clasificación multiclase en PyTorch

Para hacer clasificación con **más de dos categorías** usamos la [entropía cruzada](https://pytorch.org/docs/stable/nn.html#torch.nn.CrossEntropyLoss)

    torch.nn.CrossEntropyLoss()

Notemos que esto es distinto a la [entropía cruzada binaria](https://pytorch.org/docs/stable/nn.html#torch.nn.BCELoss) que usamos para clasificar dos clases

Si el problema de clasificación es de $M$ categorías la última capa de la red debe tener $M$ neuronas

Adicionalmente no se debe usar función de activación ya que `CrossEntropyLoss` la aplica de forma interna_____no_output_____

criterion = torch.nn.CrossEntropyLoss(reduction='sum')_____no_output_____

La función de activación que se aplica de forma interna es la [SoftMax](https://pytorch.org/docs/stable/nn.html#torch.nn.Softmax)

SoftMax recibe $M$ valores y les aplica la transformación
$$
y_i = \text{SoftMax}_i(x) = \frac{e^{x_i}}{\sum_{i=1}^M e^{x_i}}
$$

El resultado es que los $y_i \in [0, 1]$ y además $\sum_{i=1}^M y_i = 1$, es decir que lo podemos interpretar como una probabilidad_____no_output_____### Gradiente descendente con paso adaptivo

Para acelerar el entrenamiento podemos usar un algoritmo de [gradiente descendente con paso adaptivo](https://arxiv.org/abs/1609.04747)

Un ejemplo ampliamente usado es [Adam](https://arxiv.org/abs/1412.6980)

- Se utiliza la historia de los gradientes
- Se utiliza momentum (inercia)
- Cada parámetro tiene un paso distinto

    torch.optim.Adam(params,  **Parámetros de la red neuronal**
                     lr=0.001,  **Tasa de aprendizaje inicial**
                     betas=(0.9, 0.999),  **Factores de olvido de los gradientes históricos**
                     eps=1e-08, **Término para evitar división por cero**
                     weight_decay=0, **Regulariza los pesos de la red si es mayor que cero**
                     amsgrad=False **Corrección para mejorar la convergencia de Adam en ciertos casos**
                     )

**Atención**

Esta es un área de investigación activa

Papers recientes indican que Adam llega a un óptimo más rápido que SGD, pero ese óptimo podría no ser mejor que el obtenido por SGD

> Siempre prueba tus redes con distintos optimizadores
_____no_output_____

optimizer = torch.optim.Adam(model.parameters(), lr=1e-3, amsgrad=True)_____no_output_____

# [Torchvision](https://pytorch.org/docs/stable/torchvision/index.html)

Es una librería utilitaria de PyTorch que facilita considerablemente el trabajo con imágenes

- Funcionalidad para descargar sets de benchmark: MNIST, CIFAR, IMAGENET, ...
- Modelos clásicos pre-entrenados: Lenet5, AlexNet
- Funciones para importar imágenes en distintos formatos
- Funciones de transformación para hacer aumentación de datos en imágenes

#### Instalación

Usando conda o

    pip3 install torchvision

#### Ejemplo: Obtener base de datos de imágenes de dígitos manuscritos MNIST_____no_output_____

import torchvision

mnist_train_data = torchvision.datasets.MNIST(root='/home/phuijse/datasets/',
                                              train=True, download=True,
                                              transform=torchvision.transforms.ToTensor())

mnist_test_data = torchvision.datasets.MNIST(root='/home/phuijse/datasets/',
                                             train=False, download=True,
                                             transform=torchvision.transforms.ToTensor())_____no_output_____

- Imágenes de 28x28 píxeles en escala de grises
- Diez categorías: Dígitos manuscritos del cero al nueve
- 60.000 imágenes de entrenamiento, 10.000 imágenes de prueba
- Por defecto las imágenes vienen en [formato PIL](https://pillow.readthedocs.io/en/stable/) (entero 8bit), usamos la transformación [`ToTensor()`](https://pytorch.org/docs/stable/torchvision/transforms.html#torchvision.transforms.ToTensor) para convertirla a tensor en float32_____no_output_____

image, label = mnist_train_data[0]
display(len(mnist_train_data), type(image), image.dtype, type(label))
fig, ax = plt.subplots(1, 10, figsize=(8, 1.5), tight_layout=True)
idx = np.random.permutation(len(mnist_train_data))[:10]
for k in range(10):
    image, label = mnist_train_data[idx[k]]
    ax[k].imshow(image[0, :, :].numpy(), cmap=plt.cm.Greys_r)
    ax[k].axis('off');
    ax[k].set_title(label)_____no_output_____

#### Dataloaders

Creamos dataloaders de entrenamiento y validación_____no_output_____

from torch.utils.data import Subset, DataLoader
import sklearn.model_selection
# Set de entrenamiento y validación estratíficados
sss = sklearn.model_selection.StratifiedShuffleSplit(train_size=0.75).split(mnist_train_data.data, mnist_train_data.targets)
train_idx, valid_idx = next(sss)

# Data loader de entrenamiento
train_dataset = Subset(mnist_train_data, train_idx)
train_loader = DataLoader(train_dataset, shuffle=True, batch_size=32)

# Data loader de validación
valid_dataset = Subset(mnist_train_data, valid_idx)
valid_loader = DataLoader(valid_dataset, shuffle=False, batch_size=256)_____no_output_____

# Entrenamiento de la red convolucional

- Si tenemos acceso a una GPU podemos usar el atributo `.cuda()` o `.to()` para enviar el modelo y los datos a la GPU para acelerar los cálculos
- Actualizamos los parámetros en el conjunto de entrenamiento
- Medimos la convergencia en el conjunto de validación
- Guardamos el modelo con mejor eror de validación
- Usaremos [tqdm](https://tqdm.github.io/) para medir el tiempo por iteración dentro de jupyter. Instalar con [conda](https://anaconda.org/conda-forge/tqdm) o [pip](https://pypi.org/project/tqdm/)

### Visualización de entrenamiento usando tensorboard

Podemos usar la herramienta [tensorboard](https://pytorch.org/tutorials/intermediate/tensorboard_tutorial.html) para visualizar el entrenamiento de la red en vivo y/o comparar distintos entrenamientos

- Instalar tensorboard versión 1.15 o mayor con [conda](https://anaconda.org/conda-forge/tensorboard) o pip

- Escribir en un terminal

        tensorboard --logdir=/tmp/tensorboard/

- Apuntar el navegador a

        https://localhost:6006

_____no_output_____

import time
from tqdm import tqdm_notebook
from torch.utils.tensorboard import SummaryWriter

use_gpu = False
nepochs = 100
best_valid = np.inf
writer = SummaryWriter(log_dir="/tmp/tensorboard/red_convolucional_simple/"+str(time.time_ns()),
                       flush_secs=20)

# Enviar modelo a la GPU
if use_gpu:
    nnet = nnet.cuda()

for k in tqdm_notebook(range(nepochs)):
    # Entrenamiento
    epoch_loss, epoch_acc = 0.0, 0.0
    for mbdata, mblabel in train_loader:
        if use_gpu:
            mbdata, mblabel = mbdata.cuda(), mblabel.cuda()
        # Inferencia
        prediction = model.forward(mbdata)
        # Estimar el error
        loss = criterion(prediction, mblabel)
        epoch_loss += loss.item()
        epoch_acc += (torch.nn.Softmax(dim=1)(prediction).argmax(dim=1) == mblabel).sum().item()
        # Calcular gradientes
        optimizer.zero_grad()
        loss.backward()
        # Actualizar parámetros
        optimizer.step()
    # Enviar información a tensorboard
    writer.add_scalar('Train/Loss', epoch_loss/len(train_idx), k)
    writer.add_scalar('Train/Acc', epoch_acc/len(train_idx), k)
    # Validación
    epoch_loss, epoch_acc = 0.0, 0.0
    for mbdata, mblabel in valid_loader:
        if use_gpu:
            mbdata, mblabel = mbdata.cuda(), mblabel.cuda()
        # Inferencia
        prediction = model.forward(mbdata)
        # Estimar el error
        loss = criterion(prediction, mblabel)
        epoch_loss += loss.item()
        epoch_acc += (torch.nn.Softmax(dim=1)(prediction).argmax(dim=1) == mblabel).sum().item()
    # Enviar información a tensorboard
    writer.add_scalar('Valid/Loss', epoch_loss/len(valid_idx), k)
    writer.add_scalar('Valid/Acc', epoch_acc/len(valid_idx), k)

    if k % 5 == 0:
        if epoch_loss/len(valid_idx) < best_valid:
            best_valid = epoch_loss/len(valid_idx)
            torch.save({'epoca': k,
                        'model_state_dict': model.state_dict(),
                        'optimizer_state_dict': optimizer.state_dict(),
                        'loss': epoch_loss/len(valid_idx)},
                       '/home/phuijse/models/best_model.pt') # MODIFICA LA RUTA

# Retornar modelo a la CPU
if use_gpu:
    nnet = model.cpu()

writer.close()_____no_output_____

### Evaluando la red en el conjunto de test

Primero recuperamos la mejor red_____no_output_____

model = mi_red_convolucional()

model.load_state_dict(torch.load('/home/phuijse/models/best_model.pt')['model_state_dict'])_____no_output_____

Para clasificar un ejemplo de test debemos pasar la salida por una activación SoftMax_____no_output_____

image, label = mnist_test_data[np.random.randint(1000)]
# Usamos unsqueeze para convertirlo en un minibatch de 1 elemento:
y = torch.nn.Softmax(dim=1)(model.forward(image.unsqueeze(0)))

fig, ax = plt.subplots(1, 2, figsize=(5, 3))
ax[0].bar(range(10), height=y.detach().numpy()[0])
ax[0].set_xticks(range(10))
ax[1].set_title("Etiqueta: %d" %(label))
ax[1].imshow(image.numpy()[0, :, :], cmap=plt.cm.Greys_r);
ax[1].axis('off');_____no_output_____

Podemos evaluar la red en el conjunto de prueba y construir una tabla de confusión_____no_output_____

test_loader = DataLoader(mnist_test_data, shuffle=False, batch_size=512)
test_targets = mnist_test_data.targets.numpy()
prediction_test = []
for mbdata, label in test_loader:
    logits = model.forward(mbdata)
    prediction_test.append(torch.argmax(logits, dim=1).detach().numpy())

prediction_test = np.concatenate(prediction_test)

from sklearn.metrics import confusion_matrix, classification_report

cm = confusion_matrix(test_targets, prediction_test)
display(cm)

print(classification_report(test_targets, prediction_test))_____no_output_____

Una tarea muy importante es analizar los errores de la red

- Buscar ejemplos que estaban mal etiquetados
- Proponer mejoras (nuevos atributos, capas, etc)

Por ejemplo si visualizamos los que deberían ser $7$ pero son clasificados como otro dígito_____no_output_____

idx = np.where((test_targets == 9) & ~(prediction_test == 9))[0]

fig, ax = plt.subplots(1, 10, figsize=(8, 2), tight_layout=True)
for i in range(10):
    ax[i].imshow(mnist_test_data[idx[i]][0].numpy()[0, :, :], cmap=plt.cm.Greys_r)
    ax[i].set_title(prediction_test[idx[i]])
    ax[i].axis('off')_____no_output_____

### Visualizando los filtros aprendidos_____no_output_____

fig, ax = plt.subplots(1, 8, figsize=(7, 2), tight_layout=True)
w = model.conv1.weight.data.numpy()

for i in range(8):
    ax[i].imshow(w[i, 0, :, :])
    ax[i].axis('off')_____no_output_____

# Aumentación de datos

Si tenemos un dataset de imágenes muy pequeño y la red que estamos entrenando se está sobreajustando podemos intentar incrementarlo usando **transformaciones**

Si rotamos, trasladamos o cambiamos el brillo de una imagen obtendremos una nueva imagen "casi siempre" de la misma clase

*torchvision* tiene funciones implementadas para hacer transformaciones:

- [Rotación aleatoria](https://pytorch.org/docs/stable/torchvision/transforms.html#torchvision.transforms.RandomRotation)
- [Espejamiento aleatorio](https://pytorch.org/docs/stable/torchvision/transforms.html#torchvision.transforms.RandomHorizontalFlip)
- [Cropping aleatorio](https://pytorch.org/docs/stable/torchvision/transforms.html#torchvision.transforms.RandomCrop): Recortar la imagen
- [Cambios aleatorios de brillo y contraste](https://pytorch.org/docs/stable/torchvision/transforms.html#torchvision.transforms.ColorJitter)
- [Transformación afin aleatoria](https://pytorch.org/docs/stable/torchvision/transforms.html)
- [entre otros](https://pytorch.org/docs/stable/torchvision/transforms.html)

Cada transformación permite especificar límites, por ejemplo "máximo ángulo de rotación", "máxima distorsión de brillo", etc

Las transformaciones también sirven para hacer que la red gane "invarianzas"

**Ejemplo:** Si entrenamos con copias rotadas de nuestras imágenes, la red se volverá invariante a la rotación

### ATENCIÓN

> Las transformaciones que apliquemos no deben cambiar la interpretación de clase

- Si rotas un seis en 180 grados se convierte en un nueve
- Si cambias demasiado el tono (hue) podrías obtener colores distintos a la realidad (perro verde?)

### Transformaciones aleatorias con [torchvision](https://pytorch.org/docs/stable/torchvision/transforms.html)

La mayoría de las transformaciones están diseñadas para aplicarse sobre imágenes en formato PIL

Podemos componer varias transformaciones usando [`torchvision.transforms.Compose`](https://pytorch.org/docs/stable/torchvision/transforms.html)_____no_output_____

import urllib
url, filename = ("https://i.kym-cdn.com/photos/images/newsfeed/000/674/934/422.jpg", "dog.jpg")
urllib.request.urlretrieve(url, filename)

from PIL import Image

img = Image.open("dog.jpg")

transform = torchvision.transforms.Compose([torchvision.transforms.Resize(200),
                                            torchvision.transforms.RandomHorizontalFlip(),
                                            torchvision.transforms.RandomRotation(degrees=30),
                                            torchvision.transforms.ColorJitter(brightness=0.5, contrast=0.5,
                                                                               saturation=0.5, hue=0.0),
                                           ])

display(torchvision.transforms.Resize(200)(img))
display(transform(img))_____no_output_____

## Entrenando con datos aumentados

Podemos componer una transformación y añadirla a un dataset

Luego cuando usamos el dataloader se generaran imágenes con transformaciones aleatorias

### IMPORTANTE: SOLO SE AUMENTA EL CONJUNTO DE ENTRENAMIENTO_____no_output_____

transform = torchvision.transforms.Compose([torchvision.transforms.RandomAffine(degrees=30, translate=(0.2, 0.2),
                                                                                scale=(0.5, 1.5), shear=None,
                                                                                resample=False, fillcolor=0),
                                            torchvision.transforms.ColorJitter(brightness=0.5, contrast=0.5,
                                                                               saturation=0.5, hue=0.0),
                                            torchvision.transforms.ToTensor()
                                           ])

mnist_train_data = torchvision.datasets.MNIST(root='/home/phuijse/datasets/',
                                             train=True, download=True,
                                             transform=transform)

train_loader = DataLoader(mnist_train_data, shuffle=False, batch_size=32)

for image, label in train_loader:
    break

fig, ax = plt.subplots(4, 8, figsize=(7, 4), tight_layout=True)
for k in range(32):
    i, j = np.unravel_index(k, (4, 8))
    ax[i, j].axis('off')
    ax[i, j].set_title(label[k].numpy())
    ax[i, j].imshow(image[k].numpy()[0, :, :], cmap=plt.cm.Greys_r)_____no_output_____

# Utilizando un modelo pre-entrenado

[`torchvision.models`](https://pytorch.org/docs/stable/torchvision/models.html) ofrece una serie de modelos famosos de la literatura de *deep learning*

Por defecto el modelo se carga con pesos aleatorios

Se pueden escoger modelos para clasificar, localizar y segmentar

Si indicamos `pretrained=True` se descarga un modelo entrenado

Por ejemplo, podemos cargar [resnet18](https://arxiv.org/pdf/1512.03385.pdf) [pre-entrenado](https://pytorch.org/docs/stable/torchvision/models.html#torchvision.models.resnet18) en [ImageNet](http://image-net.org/) _____no_output_____

resnet_model = torchvision.models.resnet18(pretrained=True, progress=True)
resnet_model.eval()_____no_output_____

Los modelos pre-entrenados esperan imágenes con
- tres canales (RGB)
- al menos 224x224 píxeles
- píxeles entre 0 y 1 (float)
- normalizadas con

        normalize = torchvision.transforms.Normalize(mean=[0.485, 0.456, 0.406],
                                                     std=[0.229, 0.224, 0.225])

_____no_output_____

url, filename = ("https://github.com/pytorch/hub/raw/master/dog.jpg", "dog.jpg")
urllib.request.urlretrieve(url, filename)

img = Image.open("dog.jpg")

transform = torchvision.transforms.Compose([torchvision.transforms.Resize(256),
                                            torchvision.transforms.CenterCrop(224),
                                            torchvision.transforms.ToTensor(),
                                            torchvision.transforms.Normalize(mean=(0.485, 0.456, 0.406),
                                                                             std=(0.229, 0.224, 0.225))])

# La clase con probabilidad más alta
resnet_model.forward(transform(img).unsqueeze(0)).argmax(dim=1)_____no_output_____

¿A qué corresponde esta clase?

Clases de ImageNet: https://gist.github.com/ageitgey/4e1342c10a71981d0b491e1b8227328b

¿y la imagen que era?_____no_output_____

img_____no_output_____

# Resumen

Aspectos a considerar durante el entrenamiento de redes neuronales
- Arquitecturas (cantidad y organización de capas, funciones de activación)
- Funciones de costo y Optimizadores (tasa de aprendizaje)
- Verificar convergencia y sobreajuste:
    - Siempre ir guardando el mejor modelo en validación (checkpoint)
    - Detener el entrenamiento si el error de validación no disminuye
- Inicialización de los parámetros: Probar varios entrenamientos desde inicios aleatorios distintos
- Normalización de entrada y normalización capa a capa ([batch-normalization](https://pytorch.org/docs/stable/nn.html#torch.nn.BatchNorm2d))
- Si el modelo se sobreajusta pronto
    - Disminuir complejidad
    - Regularizar (Aumentación de datos, penalización de los pesos, Dropout)
- Si quiero aprovechar un modelo preentrenado
    - Transferencia de aprendizaje
    - [Zoológico de modelos](https://modelzoo.co/)
_____no_output_____# Transferencia de Aprendizaje

Ajuste fino de un modelo pre-entrenado

FUTURO_____no_output_____# Localización y segmentación

Encontrar y segmentar objetos en imágenes

FUTURO_____no_output_____


# Notebook from poojashri/toplay
Path: tabular_data_model.ipynb

_____no_output_____# The Tabular Data Model_____no_output_____

1+1_____no_output_____!mkdir data; pushd data; wget https://raw.githubusercontent.com/univai-ghf/ghfmedia/main/data/candidates.txt; wget https://raw.githubusercontent.com/univai-ghf/ghfmedia/main/data/contributors_with_candidate_id.txt; popdmkdir: cannot create directory ‘data’: File exists
/content/data /content
--2022-05-05 05:23:13--  https://raw.githubusercontent.com/univai-ghf/ghfmedia/main/data/candidates.txt
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.111.133, 185.199.109.133, 185.199.110.133, ...
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.111.133|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 388 [text/plain]
Saving to: ‘candidates.txt.1’

candidates.txt.1      0%[                    ]       0  --.-KB/s
candidates.txt.1    100%[===================>]     388  --.-KB/s    in 0s

2022-05-05 05:23:13 (28.3 MB/s) - ‘candidates.txt.1’ saved [388/388]

--2022-05-05 05:23:13--  https://raw.githubusercontent.com/univai-ghf/ghfmedia/main/data/contributors_with_candidate_id.txt
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.108.133, 185.199.109.133, 185.199.110.133, ...
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.108.133|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 14214 (14K) [text/plain]
Saving to: ‘contributors_with_candidate_id.txt.1’

contributors_with_c 100%[===================>]  13.88K  --.-KB/s    in 0s

2022-05-05 05:23:14 (82.2 MB/s) - ‘contributors_with_candidate_id.txt.1’ saved [14214/14214]

/content
!ls datacandidates.txt	  contributors_with_candidate_id.txt
candidates.txt.1  contributors_with_candidate_id.txt.1
import pandas as pd_____no_output_____from collections import OrderedDict
def make_frame(list_of_tuples, legend):
    framelist=[]
    for i, cname in enumerate(legend):
        framelist.append((cname,[e[i] for e in list_of_tuples]))
    return pd.DataFrame.from_dict(OrderedDict(framelist)) _____no_output_____

## Pandas_____no_output_____Here is what the data looks like from the file `data/candidates.txt`

```
id|first_name|last_name|middle_name|party
33|Joseph|Biden||D
36|Samuel|Brownback||R
34|Hillary|Clinton|R.|D
39|Christopher|Dodd|J.|D
26|John|Edwards||D
22|Rudolph|Giuliani||R
24|Mike|Gravel||D
16|Mike|Huckabee||R
30|Duncan|Hunter||R
31|Dennis|Kucinich||D
37|John|McCain||R
20|Barack|Obama||D
32|Ron|Paul||R
29|Bill|Richardson||D
35|Mitt|Romney||R
38|Tom|Tancredo||R
41|Fred|Thompson|D.|R
```_____no_output_____Use Pandas to read in the data_____no_output_____

dfcand=pd.read_csv("data/candidates.txt", sep='|')
dfcand_____no_output_____

Here is the other file, of contributions to candidates:

```
id|last_name|first_name|middle_name|street_1|street_2|city|state|zip|amount|date|candidate_id
|Agee|Steven||549 Laurel Branch Road||Floyd|VA|24091|500.00|2007-06-30|16
|Ahrens|Don||4034 Rennellwood Way||Pleasanton|CA|94566|250.00|2007-05-16|16
|Ahrens|Don||4034 Rennellwood Way||Pleasanton|CA|94566|50.00|2007-06-18|16
|Ahrens|Don||4034 Rennellwood Way||Pleasanton|CA|94566|100.00|2007-06-21|16
|Akin|Charles||10187 Sugar Creek Road||Bentonville|AR|72712|100.00|2007-06-16|16
|Akin|Mike||181 Baywood Lane||Monticello|AR|71655|1500.00|2007-05-18|16
|Akin|Rebecca||181 Baywood Lane||Monticello|AR|71655|500.00|2007-05-18|16
|Aldridge|Brittni||808 Capitol Square Place, SW||Washington|DC|20024|250.00|2007-06-06|16
|Allen|John D.||1052 Cannon Mill Drive||North Augusta|SC|29860|1000.00|2007-06-11|16
|Allen|John D.||1052 Cannon Mill Drive||North Augusta|SC|29860|1300.00|2007-06-29|16
|Allison|John W.||P.O. Box 1089||Conway|AR|72033|1000.00|2007-05-18|16
|Allison|Rebecca||3206 Summit Court||Little Rock|AR|72227|1000.00|2007-04-25|16
|Allison|Rebecca||3206 Summit Court||Little Rock|AR|72227|200.00|2007-06-12|16
|Altes|R.D.||8600 Moody Road||Fort Smith|AR|72903|2300.00|2007-06-21|16
|Andres|Dale||1160 Glen Oaks Drive||West Des Moines|IA|50266|250.00|2007-06-06|16
|Anthony|John||211 Long Island Drive||Hot Springs|AR|71913|2300.00|2007-06-12|16
|Arbogast|Robert||12900 State Route 56 SE||Mount Sterling|OH|43143|500.00|2007-04-08|16
|Arbogast|Robert||12900 State Route 56 SE||Mount Sterling|OH|43143|100.00|2007-06-22|16
|Ardle|William||412 Dakota Avenue||Springfield|OH|45504|50.00|2007-06-28|16
|Atiq|Omar||7200 S Hazel Street||Pine Bluff|AR|71603|1000.00|2007-05-18|16
|Atiq|Omar||7200 S Hazel Street||Pine Bluff|AR|71603|1000.00|2007-06-27|16
|Baker|David||2550 Adamsbrooke Drive||Conway|AR|72034|2300.00|2007-04-11|16
|Bancroft|David||2934 Broderick Street||San Francisco|CA|94123|250.00|2007-04-24|16
|Banks|Charles||P.O. Box 251310||Little Rock|AR|72225|1000.00|2007-05-14|16
|Barbee|John||516 Kellyridge Drive||Apex|NC|27502|500.00|2007-05-23|16
|Buckler|Steve||24351 Armada Dr||Dana Point|CA|926291306|50.00|2007-07-30|20
|Buckler|Steve||24351 Armada Dr||Dana Point|CA|926291306|25.00|2007-08-16|20
|Buckheit|Bruce||8904 KAREN DR||FAIRFAX|VA|220312731|100.00|2007-09-19|20
|Buckel|Linda||PO Box 683130||Park City|UT|840683130|2300.00|2007-08-14|20
|Buckel|Linda||PO Box 683130||Park City|UT|840683130|-2300.00|2007-08-14|20
|Buckel|Linda||PO Box 683130||Park City|UT|840683130|4600.00|2007-08-14|20
|Buck|Thomas||4206 Terrace Street||Kansas City|MO|64111|100.00|2007-09-25|20
|Buck|Jay|K.|1855 Old Willow Rd Unit 322||Northfield|IL|600932918|200.00|2007-09-12|20
|Buck|Blaine|M|45 Eaton Ave||Camden|ME|048431752|2300.00|2007-09-30|20
|Buck|Barbara||1780 NE 138th St||North Miami|FL|331811316|50.00|2007-09-13|20
|Buck|Barbara||1780 NE 138th St||North Miami|FL|331811316|50.00|2007-07-19|20
|Buchman|Mark M||2530 Lawton Ave||San Luis Obispo|CA|934015622|460.80|2007-07-18|20
|Bucher|Ida|M|1400 Warnall Ave||Los Angeles|CA|900245333|100.00|2007-07-10|20
|Buchanek|Elizabeth||7917 Kentbury Dr||Bethesda|MD|208144615|50.00|2007-09-30|20
|Buchanan|John||2025 NW 29th Rd||Boca Raton|FL|334316303|500.00|2007-09-24|20
|Buchanan|John||2025 NW 29th Rd||Boca Raton|FL|334316303|-500.00|2007-09-24|20
|Buchanan|John||2025 NW 29th Rd||Boca Raton|FL|334316303|500.00|2007-09-24|20
|Buchanan|John||2025 NW 29th Rd||Boca Raton|FL|334316303|700.00|2007-08-28|20
|Buchanan|John||2025 NW 29th Rd||Boca Raton|FL|334316303|-700.00|2007-08-28|20
|Buchanan|John||2025 NW 29th Rd||Boca Raton|FL|334316303|1000.00|2007-08-28|20
|Buchanan|John||2025 NW 29th Rd||Boca Raton|FL|334316303|1300.00|2007-08-09|20
|Buchanan|John||2025 NW 29th Rd||Boca Raton|FL|334316303|200.00|2007-08-14|20
|Buchanan|John||2025 NW 29th Rd||Boca Raton|FL|334316303|500.00|2007-07-25|20
|Buchanan|John||4635 49th St NW||Washington|DC|200164320|200.09|2007-09-23|20
|Harrison|Ryan||2247 3rd St||La Verne|CA|917504918|25.00|2007-07-26|20
|BYNUM|HERBERT||332 SUNNYSIDE ROAD||TAMPA|FL|336177249|-500.00|2008-03-10|22
|BYINGTON|MARGARET|E.|2633 MIDDLEBORO LANE N.E.||GRAND RAPIDS|MI|495061254|-2300.00|2008-03-03|22
|BYERS|BOB|A.|13170 TELFAIR AVENUE||SYLMAR|CA|913423573|-2300.00|2008-03-07|22
|BYERS|AUDREY||2658 LADBROOK WAY||THOUSAND OAKS|CA|913615073|-200.00|2008-03-07|22
|BUSH|KRYSTIE||P.O. BOX 61046||DENVER|CO|802061046|-2300.00|2008-03-06|22
|BUSH|ERIC||P.O. BOX 61046||DENVER|CO|802061046|-2300.00|2008-03-06|22
|BURTON|SUSAN||9338 DEER CREEK DRIVE||TAMPA|FL|336472286|-2300.00|2008-03-05|22
|BURTON|STEVEN|G.|9938 DEER CREEK DRIVE||TAMPA|FL|33647|-2300.00|2008-03-05|22
|BURTON|GLENN|M.|4404 CHARLESTON COURT||TAMPA|FL|336092620|-2300.00|2008-03-05|22
|BURKHARDT|CRAIG|S.|910 15TH STREET N.W.||WASHINGTON|DC|200052503|-500.00|2008-03-07|22
|BURKHARDT|CRAIG|S.|910 15TH STREET N.W.||WASHINGTON|DC|200052503|-1000.00|2008-03-07|22
|BURKHARDT|BARBARA||910 15TH STREET N.W.||WASHINGTON|DC|200052503|-500.00|2008-03-07|22
|BURKE|SUZANNE|M.|3401 EVANSTON||SEATTLE|WA|981038677|-700.00|2008-03-05|22
|BURKE|GAIL||165 E. 32ND STREET|APARTMENT 9E|NEW YORK|NY|100166014|-2000.00|2008-03-05|22
|BURKE|DONALD|J.|12 LOMPOC||RANCHO SANTA MARGA|CA|926881817|-2300.00|2008-03-11|22
|BURGERT|RONALD|L.|5723 PLUMTREE DRIVE||DALLAS|TX|752524926|-1000.00|2008-03-05|22
|BULL|BARTLE|B.|439 E. 51ST STREET||NEW YORK|NY|100226473|-800.00|2008-03-10|22
|BULL|BARTLE|B.|439 E. 51ST STREET||NEW YORK|NY|100226473|-1000.00|2008-03-10|22
|BUKOWSKI|DANIEL|J.|702 S. WRIGHT STREET||NAPERVILLE|IL|605406736|-100.00|2008-03-10|22
|BUISSON|MARGARET|A.|P.O. BOX 197029||LOUISVILLE|KY|402597029|-200.00|2008-03-11|22
|BUCKLEY|WALTER|W.|1635 COUNTRY ROAD||BETHLEHEM|PA|180155718|-100.00|2008-03-05|22
|BUCKLEY|MARJORIE|B.|1635 COUNTRY ROAD||BETHLEHEM|PA|180155718|-100.00|2008-03-05|22
|BRUNO|JOHN||10136 WINDERMERE CHASE BLVD.||GOTHA|FL|347344707|-2300.00|2008-03-06|22
|BRUNO|IRENE||10136 WINDERMERE CHASE BLVD.||GOTHA|FL|347344707|-2300.00|2008-03-06|22
|BROWN|TIMOTHY|J.|26826 MARLOWE COURT||STEVENSON RANCH|CA|913811020|-2300.00|2008-03-06|22
|Schuff|Bryan||1700 W Sweden Rd||Brockport|NY|14420|-25.00|2008-08-22|32
|Hobbs|James||229 Cherry Lane||White House|TN|37188|-25.00|2008-08-19|32
|Ranganath|Anoop||2507 Willard Drive||Charlottesville|VA|22903|-100.00|2008-04-21|32
|Nystrom|Michael|A|93A Fairmont Street||Arlington|MA|02474|-503.00|2008-04-21|32
|Muse|Nina|Jo|2915 Toro Canyon Rd||Austin|TX|78746|-50.00|2008-04-21|32
|Waddell|James|L.|1823 Spel Lane SW||Rochester|MN|55902|-28.00|2008-04-21|32
|Brucks|William|C.|PO Box 391||Corona del Mar|CA|92625|-150.00|2008-04-21|32
|Kuehn|David||14502 West 93rd Street||Lenexa|KS|66215|-330.00|2008-04-21|32
|Verster|Jeanette|M.|7220 SW 61st St||Miami|FL|331431807|-1000.00|2008-04-21|32
|Uihlein|Richard||1396 N Waukegan Rd||Lake Forest|IL|600451147|-2300.00|2008-04-21|32
|Eskenberry|Robert|P|10960 Gray Cir||Westminster|CO|80020|-223.00|2008-04-21|32
|Froehling|Alan|L.|302 Broadway St||Mount Vernon|IL|628645116|-844.80|2008-04-21|32
|Duryea|Marcia|A.|123 Bayview Ave||Amityville|NY|11701|-299.50|2008-04-21|32
|Perreault|Louise||503 Brockridge Hunt Drive||Hampton|VA|23666|-34.08|2008-04-21|32
|Rozenfeld|Timur||57 Herbert Road||Robbinsville|NJ|08691|-777.95|2008-04-21|32
|Kazor|Christopher|M|707 Spindletree ave||Naperville|IL|60565|-2592.00|2008-04-21|32
|Lehner|Thomas|S.|2701 Star Lane||Wadsworth|OH|44281|-200.00|2008-04-21|32
|Plummer|Joseph||587 Blake Hill Rd||New Hampton|NH|032564424|-24.60|2008-04-21|32
|Raught|Philip|M|4714 Plum Way||Pittsburgh|PA|15201|-1046.00|2008-04-21|32
|Ferrara|Judith|D|1508 Waterford Road||Yardley|PA|19067|-1100.00|2008-04-21|32
|Johnson|Cathleen|E.|1003 Justin Ln Apt 2016||Austin|TX|787572648|-14.76|2008-04-21|32
|Sanford|Bradley||940 Post St #43||San Francisco|CA|94109|-24.53|2008-04-21|32
|Gaarder|Bruce||PO Box 4085||Mountain Home AFB|ID|83648|-261.00|2008-04-21|32
|Choe|Hyeokchan||207 Bridle Way||Fort Lee|NJ|070246302|-39.50|2008-04-21|32
|Jacobs|Richard|G.|14337 Tawya Rd||Apple Valley|CA|923075545|-1000.00|2008-04-21|32
|Aaronson|Rebecca||2000 Village Green Dr Apt 12||Mill Creek|WA|980125787|100.00|2008-02-08|34
|Aarons|Elaine||481 Buck Island Rd Apt 17A|APT 17A|West Yarmouth|MA|026733300|25.00|2008-02-26|34
|Aarons|Elaine||481 Buck Island Rd Apt 17A|APT 17A|West Yarmouth|MA|026733300|70.00|2008-02-25|34
|Aarons|Elaine||481 Buck Island Rd Apt 17A|APT 17A|West Yarmouth|MA|026733300|100.00|2008-02-08|34
|Aaron|Shirley||101 Cherry Ave||Havana|FL|323331311|50.00|2008-02-29|34
|Aaron|Shirley||101 Cherry Ave||Havana|FL|323331311|100.00|2008-02-29|34
|Aaronson|Rebecca||2000 Village Green Dr Apt 12||Mill Creek|WA|980125787|100.00|2008-02-14|34
|Aaron|Shirley||101 Cherry Ave||Havana|FL|323331311|100.00|2008-02-24|34
|Aaron|Shirley||101 Cherry Ave||Havana|FL|323331311|100.00|2008-02-22|34
|Aaron|Shirley||101 Cherry Ave||Havana|FL|323331311|100.00|2008-02-17|34
|Reid|Elizabeth||73 W Patent Rd|OPHIR FARM NORTH|Bedford Hills|NY|105072222|-350.00|2008-08-28|34
|Reich|Thomas||499 Park Ave||New York|NY|100221240|-2300.00|2008-08-28|34
|Aaron|Shirley||101 Cherry Ave||Havana|FL|323331311|100.00|2008-02-08|34
|Aaron|Shirley||101 Cherry Ave||Havana|FL|323331311|100.00|2008-02-03|34
|Aaron|Sharron||1804 E Montgomery St||Broken Arrow|OK|740121840|500.00|2008-02-09|34
|Aaron|Patricia||418 NW 35th St||Oklahoma City|OK|731188602|200.00|2008-02-26|34
|Aaron|Patricia||418 NW 35th St||Oklahoma City|OK|731188602|100.00|2008-02-12|34
|Aaron|Jim||2178 Fairway Cir||Canton|MI|481885097|300.00|2008-02-07|34
|Aaron|Jim||2178 Fairway Cir||Canton|MI|481885097|200.00|2008-02-29|34
|Aaron|Carole||PO Box 1806||Ogunquit|ME|039071806|70.00|2008-02-29|34
|Aaron|Carole||PO Box 1806||Ogunquit|ME|039071806|50.00|2008-02-07|34
|Aaron|Carole||PO Box 1806||Ogunquit|ME|039071806|100.00|2008-02-03|34
|Aaron|Barbara||2298 Pacific Ave # 6||San Francisco|CA|941151435|1000.00|2008-02-11|34
|Aanonsen|Lin||897 Raymond Ave||Saint Paul|MN|551141508|250.00|2008-02-21|34
|Aanonsen|Lin||897 Raymond Ave||Saint Paul|MN|551141508|100.00|2008-02-08|34
|BOURNE|TRAVIS||LAGE KAART 77||BRASSCHATT||02930|-500.00|2008-11-20|35
|SECRIST|BRIAN|L.|3 MULE DEER TRAIL||LITTLETON|CO|801275722|-1000.00|2008-04-07|35
|TOLLESTRUP|TRAVIS|W.|16331 WINECREEK RD.||SAN DIEGO|CA|92127|-1000.00|2008-05-15|35
|ACCORD|DEAN|C.|8813 ROBINSON RIDGE ROAD||LAS VEGAS|NV|891175812|500.00|2007-07-13|35
|ABTS|HENRY||P. O. BOX 7299||INCLINE VILLAGE|NV|894527299|100.00|2007-07-13|35
|ABSHIER|LANNY||14191 S.E. HIGHWAY 301||SUMMERFIELD|FL|34491|500.00|2007-09-25|35
|ABSHIER|DIANA||14191 S.E. HIGHWAY 301||SUMMERFIELD|FL|34491|500.00|2007-09-25|35
|ABREU|KEVIN|M.|1305 GARDEN GLEN LANE||PEARLAND|TX|775816547|50.00|2007-09-30|35
|ABREU|KEVIN|M.|1305 GARDEN GLEN LANE||PEARLAND|TX|775816547|150.00|2007-08-09|35
|ABREU|KEVIN|M.|1305 GARDEN GLEN LANE||PEARLAND|TX|775816547|50.00|2007-07-19|35
|ABRAMOWITZ|NIRA||411 HARBOR ROAD||SOUTHPORT|CT|068901376|2300.00|2007-09-14|35
|ABRAMS|MICHAEL||7910 WOODMONT AVENUE||BETHESDA|MD|208143002|250.00|2007-09-29|35
|ABRAMOWITZ|KEN||200 CENTRAL PARK S.|APARTMENT 31A|NEW YORK|NY|100191448|300.00|2007-09-11|35
|ABOUBAKARE|NASAR||1400 SAN MIGUEL DRIVE||CORONA DEL MAR|CA|926251300|1000.00|2007-07-09|35
|ABEGG|PATRICIA|T.|1862 E. 5150 S.||SALT LAKE CITY|UT|841176911|75.00|2007-09-25|35
|ABEGG|PATRICIA|T.|1862 E. 5150 S.||SALT LAKE CITY|UT|841176911|25.00|2007-09-17|35
|ABEGG|PATRICIA|T.|1862 E. 5150 S.||SALT LAKE CITY|UT|841176911|75.00|2007-08-31|35
|ABEGG|PATRICIA|T.|1862 E. 5150 S.||SALT LAKE CITY|UT|841176911|75.00|2007-08-14|35
|ABEGG|PATRICIA|T.|1862 E. 5150 S.||SALT LAKE CITY|UT|841176911|25.00|2007-08-06|35
|ABEGG|PATRICIA|T.|1862 E. 5150 S.||SALT LAKE CITY|UT|841176911|25.00|2007-07-10|35
|ABDELLA|THOMAS|M.|4231 MONUMENT WALL WAY #340||FAIRFAX|VA|220308440|50.00|2007-09-30|35
|ABBOTT|WELDON|S.|777 EAST SOUTH TEMPLE  4E||SALT LAKE CITY|UT|841021269|100.00|2007-09-29|35
|ABBOTT|WELDON|S.|777 EAST SOUTH TEMPLE  4E||SALT LAKE CITY|UT|841021269|50.00|2007-08-09|35
|ABBOTT|GERALD|F.|389 BENEFIT STREET||PROVIDENCE|RI|029032946|100.00|2007-09-15|35
|ABBOTT|GERALD|F.|389 BENEFIT STREET||PROVIDENCE|RI|029032946|100.00|2007-08-15|35
|ABEDIN|ZAINUL||715 N. CENTRAL AVENUE|SUITE 212|GLENDALE|CA|912031164|500.00|2008-01-21|37
|ABBOTT|SYBIL|F.|446 GAMES DRIVE||RENO|NV|895093326|75.00|2008-01-08|37
|ABBOTT|SYBIL|F.|446 GAMES DRIVE||RENO|NV|895093326|50.00|2008-01-08|37
|ABBOTT|RONALD|LEANDER|5453 HAWTHORNE STREET||MONTCLAIR|CA|917632551|200.00|2008-01-31|37
|ABBOTT|RONALD|LEANDER|5453 HAWTHORNE STREET||MONTCLAIR|CA|917632551|100.00|2008-01-08|37
|ABBOTT|ROBERT|A.|3061 LOREE ROAD||DECKERVILLE|MI|484279763|500.00|2008-01-21|37
|ABBOTT|MIKE|E.|4516 OSPREY LNDG||NICEVILLE|FL|325786810|1000.00|2008-01-15|37
|ABBOT|DAVID|M.|56 SALEM STREET||ANDOVER|MA|018102114|200.00|2008-01-21|37
|ABBO|PAULINE|MORENCY|10720 JACOB LANE||WHITE LAKE|MI|483862274|35.00|2008-01-07|37
|ABATE|MARIA|ELENA|1291 NIGHTINGALE AVENUE||MIAMI SPRINGS|FL|331663832|2600.00|2008-01-25|37
|ABAIR|PETER||40 EVANS STREET||WATERTOWN|MA|024722150|25.00|2008-01-09|37
|ABACHERLI|SHIRLEY|M.|29875 NEWPORT ROAD||MENIFEE|CA|925849524|150.00|2008-01-28|37
|AARONS|CHARLES||1730 SHORE DRIVE||ANCHORAGE|AK|995153207|300.00|2008-01-30|37
|AARONS|CHARLES||1730 SHORE DRIVE||ANCHORAGE|AK|995153207|410.00|2008-01-15|37
|AARONS|CHARLES||1730 SHORE DRIVE||ANCHORAGE|AK|995153207|500.00|2008-01-09|37
|ABEL|JOHN|H.|422 THOMAS STREET||BETHLEHEM|PA|180153316|200.00|2008-01-22|37
|ABEL|MARLING|L.|14 HANGING MOSS LANE||GREENVILLE|SC|296155069|100.00|2008-01-22|37
|ABEL|RUDOLPH||4532 OCEAN BLVD.|# 108|SARASOTA|FL|342421337|100.00|2008-01-08|37
|ABELE|RODNEY||3620 METAIRIE HEIGHTS AVENUE||METAIRIE|LA|700021823|500.00|2008-01-15|37
|ABERCROMBIE|DENIS||11811 WATER OAK CT||MAGNOLIA|TX|773546270|500.00|2008-01-30|37
|ABESHAUS|MERRILL|M.|1801 N. HEREFORD DRIVE||FLAGSTAFF|AZ|860011121|120.00|2008-01-16|37
|ABRAHAM|GEORGE||P.O. BOX 1504||LAKE CHARLES|LA|706021504|800.00|2008-01-17|37
|ABRAHAMSON|PETER|J.|1030 W. ROSCOE STREET||CHICAGO|IL|606572207|50.00|2008-01-25|37
|ABRAHAM|SALEM|A.|P.O. BOX 7||CANADIAN|TX|790140007|1000.00|2008-01-17|37
|ABRAHAM|SALEM|A.|P.O. BOX 7||CANADIAN|TX|790140007|1300.00|2008-01-30|37
```_____no_output_____

dfcwci=pd.read_csv("data/contributors_with_candidate_id.txt", sep="|")
dfcwci.head()_____no_output_____

You'll notice that the contributions dont have the first column, so we will need to be cleaning things up a bit..._____no_output_____

del dfcwci['id']
dfcwci.head()_____no_output_____

## SQL and Relational Databases_____no_output_____Lets start with Relational Databases, so called because they contain "relations" (tables), which are SETS of "tuples" (rows) which map "attributes" (columns) to atomic values.

The available attributes are constrained by a "header" tuple of attributes which set the type. We do this below here, using the SQL language to set things up._____no_output_____```sql
DROP TABLE IF EXISTS "candidates";
DROP TABLE IF EXISTS "contributors";
CREATE TABLE "candidates" (
    "id" INTEGER PRIMARY KEY  NOT NULL ,
    "first_name" VARCHAR,
    "last_name" VARCHAR,
    "middle_name" VARCHAR,
    "party" VARCHAR NOT NULL
);
CREATE TABLE "contributors" (
    "id" INTEGER PRIMARY KEY  AUTOINCREMENT  NOT NULL,
    "last_name" VARCHAR,
    "first_name" VARCHAR,
    "middle_name" VARCHAR,
    "street_1" VARCHAR,
    "street_2" VARCHAR,
    "city" VARCHAR,
    "state" VARCHAR,
    "zip" VARCHAR, -- Notice that we are converting the zip from integer to string
    "amount" INTEGER,
    "date" DATETIME,
    "candidate_id" INTEGER NOT NULL,
    FOREIGN KEY(candidate_id) REFERENCES candidates(id)
);
```_____no_output_____

ourschema="""
DROP TABLE IF EXISTS "candidates";
DROP TABLE IF EXISTS "contributors";
CREATE TABLE "candidates" (
    "id" INTEGER PRIMARY KEY  NOT NULL ,
    "first_name" VARCHAR,
    "last_name" VARCHAR,
    "middle_name" VARCHAR,
    "party" VARCHAR NOT NULL
);
CREATE TABLE "contributors" (
    "id" INTEGER PRIMARY KEY  AUTOINCREMENT  NOT NULL,
    "last_name" VARCHAR,
    "first_name" VARCHAR,
    "middle_name" VARCHAR,
    "street_1" VARCHAR,
    "street_2" VARCHAR,
    "city" VARCHAR,
    "state" VARCHAR,
    "zip" VARCHAR, -- Notice that we are converting the zip from integer to string
    "amount" INTEGER,
    "date" DATETIME,
    "candidate_id" INTEGER NOT NULL,
    FOREIGN KEY(candidate_id) REFERENCES candidates(id)
);
"""_____no_output_____

### SQLITE_____no_output_____We use sqlite here (and recommend Postgres for production purposes). Sqlite is a on-file database, as opposed to other common databases such as Oracle and Postgres, which run as different processes on your system. Sqlite is great for on-disk large databases which wont fit into memory.

Its also built into Python, but to use the [command line tool](https://www.sqlite.org/cli.html), I recommend you install it: https://www.sqlite.org/download.html. I also recommend you download and install the sqlite browser: http://sqlitebrowser.org .

Python implements a standard database API over all databases. Its called [DBAPI2](http://cewing.github.io/training.codefellows/lectures/day21/intro_to_dbapi2.html). It works across many SQL databases.

There is an even higher level API available, called [SQLAlchemy](http://www.sqlalchemy.org). While we wont use it here, I thoroughly recommend it. Many things in Pandas use it to interface with databases. Here we'll get away with things by using SQLITE._____no_output_____We'll write some functions to connect to Sqlite.

(1) Connect and get a DBAPI2 connection._____no_output_____

from sqlite3 import dbapi2 as sq3
from pathlib import Path
PATHSTART="."
def get_db(dbfile):
    sqlite_db = sq3.connect(Path(PATHSTART) / dbfile)
    return sqlite_db_____no_output_____

(2) Set up the database with tables. Drop tables if they exist and create them._____no_output_____

def init_db(dbfile, schema):
    """Creates the database tables."""
    db = get_db(dbfile)
    db.cursor().executescript(schema)
    db.commit()
    return db_____no_output_____

Initializing the database:_____no_output_____

db=init_db("/tmp/cancont.db", ourschema)_____no_output_____

### Populating with Pandas!!_____no_output_____

dfcand.to_sql("candidates", db, if_exists="append", index=False)_____no_output_____dfcwci.to_sql("contributors", db, if_exists="append", index=False)_____no_output_____

Now we can do queries in the lingua franca of databases: SQL_____no_output_____

sel="""
SELECT * FROM candidates;
"""
c=db.cursor().execute(sel)_____no_output_____c.fetchall()_____no_output_____def make_query(sel):
    c=db.cursor().execute(sel)
    return c.fetchall()_____no_output_____make_query("SELECT * FROM candidates;")_____no_output_____make_query("PRAGMA table_info(candidates);")_____no_output_____make_query("PRAGMA table_info(contributors);")_____no_output_____candidate_cols = [e[1] for e in make_query("PRAGMA table_info(candidates);")]
candidate_cols_____no_output_____contributor_cols = [e[1] for e in make_query("PRAGMA table_info(contributors);")]
contributor_cols_____no_output_____out=make_query("SELECT * FROM candidates;")
make_frame(out, legend=candidate_cols)_____no_output_____

## THE GRAMMAR OF DATA_____no_output_____Let us now focus on core data manipulation commands. The reason to do this is that they are *universal across systems, and by identifying them, we can quickly ask how to do these* when we encounter a new system._____no_output_____See https://gist.github.com/TomAugspurger/6e052140eaa5fdb6e8c0/ which has a comparison of r/dplyr and pandas. I stole and modified this table from there:

``dplyr`` has a small set of nicely defined verbs, which Hadley Wickham has used to identify core data manipulation commands. Here are listed the closest SQL and Pandas verbs, so we can see the universality of these manipulations.

    VERB
    dplyr
    pandas
    SQL

    QUERY/SELECTION
    filter() (and slice())
    query() (and loc[], iloc[])
    SELECT WHERE

    SORT
    arrange()
    sort_values()
    ORDER BY

    SELECT-COLUMNS/PROJECTION
    select() (and rename())
    [](__getitem__) (and rename())
    SELECT COLUMN

    SELECT-DISTINCT
    distinct()
    unique(),drop_duplicates()
    SELECT DISTINCT COLUMN

    ASSIGN
    mutate() (and transmute())
    assign
    ALTER/UPDATE

    AGGREGATE
    summarise()
    describe(), mean(), max()
    None, AVG(),MAX()

    SAMPLE
    sample_n() and sample_frac()
    sample()
    implementation dep, use RAND()

    GROUP-AGG
    group_by/summarize
    groupby/agg, count, mean
    GROUP BY

    DELETE
    ?
    drop/masking
    DELETE/WHERE

_____no_output_____### QUERY

We'll want to make queries on columns that are both numerical and categorical, combining these queries when appropriate..._____no_output_____

dfcwci.query("20 <= amount <= 40")_____no_output_____out=make_query("SELECT * FROM contributors WHERE amount BETWEEN 20 AND 40;")
make_frame(out, legend=contributor_cols)_____no_output_____

You can combine queries on different columns (here a numerical and a categorical):_____no_output_____

dfcwci.query("state=='VA' & amount < 400")_____no_output_____dfcwci[(dfcwci.state=='VA') & (dfcwci.amount < 400)]_____no_output_____out=make_query("SELECT * FROM contributors WHERE state='VA' AND amount < 400;")
make_frame(out, legend=contributor_cols)_____no_output_____

Queries that look if things are in a list...useful for categorical variables or discrete valued numerical variables..._____no_output_____

dfcwci[dfcwci.state.isin(['VA','WA'])].head(10)_____no_output_____out=make_query("SELECT * FROM contributors WHERE state IN ('VA','WA');")
make_frame(out, legend=contributor_cols).head(10)_____no_output_____

Looking for data problems with nulls:_____no_output_____

dfcwci[dfcwci.state.isnull()]_____no_output_____out=make_query("SELECT * FROM contributors WHERE state IS NULL;")
make_frame(out, legend=contributor_cols)_____no_output_____

Or the other way..._____no_output_____

dfcwci[dfcwci.state.notnull()]_____no_output_____out=make_query("SELECT * FROM contributors WHERE state IS NOT NULL;")
make_frame(out, legend=contributor_cols)_____no_output_____

### SORT AND LIMIT

After possibly making a query, we want to sort our results..._____no_output_____

dfcwci.sort_values("amount").head(10)_____no_output_____dfcwci.sort_values("amount", ascending=False).head(10)_____no_output_____out=make_query("SELECT * FROM contributors ORDER BY amount;")
make_frame(out, legend=contributor_cols).head(10)_____no_output_____out=make_query("SELECT * FROM contributors ORDER BY amount DESC;")
make_frame(out, legend=contributor_cols).head(10)_____no_output_____dfcwci[0:10] #.head(10)_____no_output_____out=make_query("SELECT * FROM contributors LIMIT 10;")
make_frame(out, legend=contributor_cols).head(10)_____no_output_____

### SELECT-COLUMNS

Sometimes we only want to get a few columns, not the entire table_____no_output_____

dfcwci[['first_name', 'amount']].head(10)_____no_output_____out=make_query("SELECT first_name, amount FROM contributors;")
make_frame(out,['first_name', 'amount']).head(10)_____no_output_____

### AGGREGATE_____no_output_____

dfcwci.describe()_____no_output_____dfcwci.amount.max()_____no_output_____

SQL is actually simpler_____no_output_____

out=make_query("SELECT MAX(amount) FROM contributors;")
out_____no_output_____

Aso `MIN`, `SUM`, `AVG`._____no_output_____

out=make_query("SELECT *, MAX(amount) AS maxamt FROM contributors;")
print(out)
make_frame(out, legend=contributor_cols+['maxamt'])[(31, 'Buckel', 'Linda', None, 'PO Box 683130', None, 'Park City', 'UT', '840683130', 4600, '2007-08-14', 20, 4600)]

As a sneak preview of a "subselection" feature we'll talk about when we talk about relationships in databases next week, we can use subselects to implement an algebraic style query..._____no_output_____

out=make_query("SELECT * FROM contributors WHERE amount > (select (MAX(amount) - 2300) FROM contributors);")
make_frame(out, legend=contributor_cols)_____no_output_____

### GROUP-AGG

Being able to group data by the value of any column is critical functionality. This is how you understand your sampling, and the distributions of various quantities in your data._____no_output_____

dfcwci.groupby("state").sum()_____no_output_____dfcwci.groupby("state")['amount'].mean()_____no_output_____out=make_query("SELECT state,SUM(amount) FROM contributors GROUP BY state;")
make_frame(out, legend=['state','sum'])_____no_output_____

## Creation and Alteration_____no_output_____So far, when we created the database, we did it using Pandas. Clearly, one ought to be able to populate a SQL database using SQL. We now turn to this use case, as well as the alteration of databases._____no_output_____### Populate with SQL INSERT_____no_output_____Before we populate a the `candidates` table, lets delete all data from it..._____no_output_____

rem="""
DELETE FROM candidates;
"""
c=db.cursor().execute(rem)
db.commit()_____no_output_____

Once again, lets look at the structure of the candidates file.

Here is what the data looks like from the file `data/candidates.txt`

```
id|first_name|last_name|middle_name|party
33|Joseph|Biden||D
36|Samuel|Brownback||R
34|Hillary|Clinton|R.|D
39|Christopher|Dodd|J.|D
26|John|Edwards||D
22|Rudolph|Giuliani||R
24|Mike|Gravel||D
16|Mike|Huckabee||R
30|Duncan|Hunter||R
31|Dennis|Kucinich||D
37|John|McCain||R
20|Barack|Obama||D
32|Ron|Paul||R
29|Bill|Richardson||D
35|Mitt|Romney||R
38|Tom|Tancredo||R
41|Fred|Thompson|D.|R
```

We compose an insertion template using the SQL insertion command..._____no_output_____

ins="""
INSERT INTO candidates (id, first_name, last_name, middle_name, party) \
    VALUES (?,?,?,?,?);
"""_____no_output_____

Now we read the file line by line, not including the header line and slurp in the data. Notice that we only finish the transaction after we have slurped in all the lines. So its all lines or none. When we execute the cursor, the question marks are used a templates with a tuple provided in.._____no_output_____

with open("data/candidates.txt") as fd:
    slines =[l.strip().split('|') for l in fd.readlines()]
    for line in slines[1:]:
        theid, first_name, last_name, middle_name, party = line
        print(theid, first_name, last_name, middle_name, party)
        valstoinsert = (int(theid), first_name, last_name, middle_name, party)
        print(ins, valstoinsert)
        db.cursor().execute(ins, valstoinsert)

db.commit()    33 Joseph Biden  D

INSERT INTO candidates (id, first_name, last_name, middle_name, party)     VALUES (?,?,?,?,?);
 (33, 'Joseph', 'Biden', '', 'D')
36 Samuel Brownback  R

INSERT INTO candidates (id, first_name, last_name, middle_name, party)     VALUES (?,?,?,?,?);
 (36, 'Samuel', 'Brownback', '', 'R')
34 Hillary Clinton R. D

INSERT INTO candidates (id, first_name, last_name, middle_name, party)     VALUES (?,?,?,?,?);
 (34, 'Hillary', 'Clinton', 'R.', 'D')
39 Christopher Dodd J. D

INSERT INTO candidates (id, first_name, last_name, middle_name, party)     VALUES (?,?,?,?,?);
 (39, 'Christopher', 'Dodd', 'J.', 'D')
26 John Edwards  D

INSERT INTO candidates (id, first_name, last_name, middle_name, party)     VALUES (?,?,?,?,?);
 (26, 'John', 'Edwards', '', 'D')
22 Rudolph Giuliani  R

INSERT INTO candidates (id, first_name, last_name, middle_name, party)     VALUES (?,?,?,?,?);
 (22, 'Rudolph', 'Giuliani', '', 'R')
24 Mike Gravel  D

INSERT INTO candidates (id, first_name, last_name, middle_name, party)     VALUES (?,?,?,?,?);
 (24, 'Mike', 'Gravel', '', 'D')
16 Mike Huckabee  R

INSERT INTO candidates (id, first_name, last_name, middle_name, party)     VALUES (?,?,?,?,?);
 (16, 'Mike', 'Huckabee', '', 'R')
30 Duncan Hunter  R

INSERT INTO candidates (id, first_name, last_name, middle_name, party)     VALUES (?,?,?,?,?);
 (30, 'Duncan', 'Hunter', '', 'R')
31 Dennis Kucinich  D

INSERT INTO candidates (id, first_name, last_name, middle_name, party)     VALUES (?,?,?,?,?);
 (31, 'Dennis', 'Kucinich', '', 'D')
37 John McCain  R

INSERT INTO candidates (id, first_name, last_name, middle_name, party)     VALUES (?,?,?,?,?);
 (37, 'John', 'McCain', '', 'R')
20 Barack Obama  D

INSERT INTO candidates (id, first_name, last_name, middle_name, party)     VALUES (?,?,?,?,?);
 (20, 'Barack', 'Obama', '', 'D')
32 Ron Paul  R

INSERT INTO candidates (id, first_name, last_name, middle_name, party)     VALUES (?,?,?,?,?);
 (32, 'Ron', 'Paul', '', 'R')
29 Bill Richardson  D

INSERT INTO candidates (id, first_name, last_name, middle_name, party)     VALUES (?,?,?,?,?);
 (29, 'Bill', 'Richardson', '', 'D')
35 Mitt Romney  R

INSERT INTO candidates (id, first_name, last_name, middle_name, party)     VALUES (?,?,?,?,?);
 (35, 'Mitt', 'Romney', '', 'R')
38 Tom Tancredo  R

INSERT INTO candidates (id, first_name, last_name, middle_name, party)     VALUES (?,?,?,?,?);
 (38, 'Tom', 'Tancredo', '', 'R')
41 Fred Thompson D. R

INSERT INTO candidates (id, first_name, last_name, middle_name, party)     VALUES (?,?,?,?,?);
 (41, 'Fred', 'Thompson', 'D.', 'R')
make_query("SELECT * FROM candidates;")_____no_output_____

### Create new columns with ASSIGN

In Pandas it is simple to create a new column (`pd.Series`) in the dataframe:_____no_output_____

dfcwci['name']=dfcwci['last_name']+", "+dfcwci['first_name']
dfcwci.head(10)_____no_output_____

One can also use `assign`, which creates a new dataframe..._____no_output_____

newdf = dfcwci.assign(ucname=dfcwci.last_name+":"+dfcwci.first_name).head(10)
newdf.head()_____no_output_____

Will the above command actually change `dfcwci`? `assign` creates a whole new dataframe. The dictionary style assigns in memory.

**What if we wanted to change an existing assignment?**

In other words, we are not creating a new column, but rather changing the values associated with a column, based on some criterion_____no_output_____

dfcwci[dfcwci.state=='VA']_____no_output_____

Lets get the `name` column we just created..._____no_output_____

dfcwci.loc[dfcwci.state=='VA', 'name']_____no_output_____

Now we set the names from Virginia to be "junk":_____no_output_____

dfcwci.loc[dfcwci.state=='VA', 'name']="junk"_____no_output_____dfcwci.query("state=='VA'")['name']_____no_output_____

---

Let us see the entire process in SQL_____no_output_____

alt="ALTER TABLE contributors ADD COLUMN name;"
db.cursor().execute(alt)
db.commit()_____no_output_____make_query("PRAGMA table_info(contributors);")_____no_output_____out = make_query("SELECT id, last_name,first_name from contributors;")
out2 = [(e[1]+", "+e[2],e[0]) for e in out]
out2[:5]_____no_output_____alt2="UPDATE contributors SET name = ? WHERE id = ?;"
for ele in out2:
    db.cursor().execute(alt2, ele)
db.commit()_____no_output_____out=make_query("SELECT * from contributors;")
print(out[0])
make_frame(out,legend=contributor_cols+["name"]).head(10)(1, 'Agee', 'Steven', None, '549 Laurel Branch Road', None, 'Floyd', 'VA', '24091', 500, '2007-06-30', 16, 'Agee, Steven')

And lets now do an assignment to an existing column_____no_output_____

upd="UPDATE contributors SET name = 'junk' WHERE state = 'VA';"
db.cursor().execute(upd)
db.commit()_____no_output_____out=make_query("SELECT * from contributors where state='VA';")
make_frame(out,contributor_cols+["name"]).head(10)_____no_output_____

## DELETE

Deletion can be based on a criterion, or one might delete an entire column, re even a table (DROP Table)_____no_output_____### No DROP COLUMN in SQLITE

Its available in other databases. Here you must just re-create your database, or know about this gotcha from the start. (reasons here: https://www.quora.com/Why-cant-SQLite-drop-columns)_____no_output_____

alt="ALTER TABLE contributors DROP COLUMN name;"
db.cursor().execute(alt)
db.commit()_____no_output_____

Its much simpler in Pandas, of-course_____no_output_____

del dfcwci['name']_____no_output_____

But you might want to DELETE, just as in a SELECT, based on a criterion..._____no_output_____

dfcwci=dfcwci[dfcwci.last_name!='Ahrens']
dfcwci.head(10)_____no_output_____drow="DELETE FROM contributors WHERE last_name=\"Ahrens\";"
db.cursor().execute(drow)
db.commit()
out=make_query("SELECT * FROM contributors;")
make_frame(out, legend=contributor_cols).head(10)_____no_output_____db.close()_____no_output_____