Exámen 2

Este exámen se enfoca en dos temas, gráficos y ajustes, los puntos a desarrollar son los siguientes. En una carpeta con su nombre encontrará:

  1. 3 gráficos en orden de dificultad, su tarea es escribir el código requerido para generar dicho gráfico, incluyendo títulos en los ejes, leyenda y grilla, la función a la que obedecen los datos con los que se genera el gráfico se puede encontrar en la leyenda.

  2. 3 conjuntos de datos también en orden de dificultad (data00.txt el más fácil) debe cargar estos datos usando la función x, y = np.loadtxt('data##.txt', unpack=True) para despues ajustarlos usando una de las técnicas vistas en clase. Si abre el archivo .txt encontrará en el encabezado una pista sobre la función de la que provienen los datos. Para este punto debe:

    • Proporcionar los parámetros de ajuste correctos para cada uno de los conjuntos de datos.
    • Realizar un gráfico con los datos originales (puntos) y el ajuste de la función, tal como hemos hecho en repetidas ocaciones.
  3. 2 compuestos para los cuales deberá proveer un análisis TGA, incluyendo, identificación de componentes, cuantificación de los componentes en masa, porcentaje de error con respecto a la temperatura ideal y gráficos asociados.

Cada uno de los 3 puntos tendrá el mismo valor sobre la nota final.

A continuación se presenta el código usado para generar los parciales (sin embargo, se oculta la semilla utilizada).


In [1]:
%matplotlib inline
from matplotlib.backends.backend_pdf import PdfPages
import matplotlib.pyplot as plt
import numpy as np
import os
import json

In [2]:
np.random.seed(12)

GUYS = [
    'Federico',
    'Sebastian Alejandro',
    'Fernando',
    'Ivan Dario',
    'Sebastian',
    'Christian David',
    'Sergio Alejandro',
    'Juan Camilo',
    'Angel Santiago',
    'Anngie Carolina',
    'Cristian Elias',
    'Juan Sebastian',
    'Daniela',
    'Maria Victoria',
    'Laura Victoria',
    'Maria Camila',
    'Oscar (Bolaños)',
]

In [3]:
PARTIAL_DIR = 'exam2'
SOLUTIONS_DIR = 'exam2_solutions'

In [4]:
def create_missing_dirs(*dirs):
    for folder in dirs:
        if not os.path.exists(folder):
            os.makedirs(folder)
create_missing_dirs(PARTIAL_DIR, SOLUTIONS_DIR)

In [5]:
def random_params(number, min_val=1, max_val=5):
    return tuple(
        np.round(
            np.random.uniform(min_val, max_val, size=number),
            decimals=1
        )
    )

In [6]:
def random_style():
    style = np.random.choice(['-', '-.', '--'], p=[.50, .30, .20])
    color = np.random.choice(['blue', 'green', 'red'])
    return style, color

In [7]:
def plot(x, y, label, plot_command=None):
    if plot_command is None:
        plot_command=plt.plot
    style, color = random_style()
    plot_command(x, y, style, label=label, color=color)
    plt.xlabel(r'$x$')
    plt.ylabel(r'$y$')
    plt.grid()
    plt.legend(loc='best')

In [8]:
def cos_plot():
    a, omega, phi = random_params(3)
    x = np.linspace(-10, 10, 200)
    y =  a * np.cos(omega * x + phi)
    plot(x, y,
         r'$y = {:.1f} \, \cos({:.1f} x + {:.1f})$'.format(a, omega, phi))

def line_plot():
    a, b = random_params(2)
    x = np.linspace(-10, 10)
    y = a * x + b
    plot(x, y, label=r'$y = {:.1f} x + {:.1f}$'.format(a, b))

def parable_plot():
    a, b, c = random_params(3)
    x = np.linspace(-10, 10)
    y = a * x ** 2 + b * x + c
    plot(x, y,
         label=r'$y = {:.1f} x ^ 2 + {:.1f} x + {:.1f}$'.format(a, b, c))

def sqrt_plot():
    a, = random_params(1)
    x = np.linspace(0, 10)
    y = a * np.sqrt(x)
    plot(x, y, label=r'$y = {:.1f} \sqrt{{x}}$'.format(a))

def exponential_plot():
    a, n, = random_params(2, min_val=0.5, max_val=2)
    x = np.linspace(0, 10)
    y = a * x ** n
    plot(x, y, label=r'$y = {:.1f} x ^ {{{:.1f}}}$'.format(a, n))
    
def exponential_loglog_plot():
    a, n, = random_params(2)
    x = np.linspace(0, 10)
    y = a * x ** n
    plot(x, y,
         label=r'$y = {:.1f} x ^ {{{:.1f}}}$'.format(a, n),
         plot_command=plt.loglog)

EASY_PLOTS = [
    cos_plot,
    line_plot,
    parable_plot,
    sqrt_plot,
    exponential_plot,
    exponential_loglog_plot,
]

In [9]:
def hard_plot():
    components = np.random.choice(EASY_PLOTS, 2, replace=False)
    for i, p in enumerate(components):
        plt.subplot(1, 2, i + 1)
        p()
        plt.tight_layout()

HARD_PLOTS = [
    hard_plot
    for _ in range(10)
]

In [10]:
def harder_plot():
    p1, p2, p3 = np.random.choice(EASY_PLOTS, 3, replace=False)
    plt.subplot(2, 1, 1)
    p1()
    plt.subplot(2, 2, 3)
    p2()
    plt.subplot(2, 2, 4)
    p3()
    plt.tight_layout()

HARDER_PLOTS = [
    harder_plot
    for _ in range(10)
]

In [11]:
def get_x():
    return np.linspace(0, 20, 1000)

In [12]:
def line_data():
    a, b = random_params(2)
    x = get_x()
    y = a * x + b
    return x, y, '$y = a x + b$', ('line', a, b)

def parable_data():
    a, b, c = random_params(3)
    x = get_x()
    y = a * x ** 2 + b * x + c
    return x, y, r'$y = a x^2 + bx + c$', ('parable', a, b, c)

def sqrt_data():
    a, = random_params(1)
    x = get_x()
    y = a * np.sqrt(x)
    return x, y, r'$y = a \sqrt{x}$', ('sqrt', a)

def exponential_data():
    a, n, = random_params(2, min_val=0.5, max_val=2)
    x = get_x()
    y = a * x ** n
    return x, y, r'$y = a x ^ n$', ('exponential', a, n)

def exponential_data():
    a, n, = random_params(2, min_val=0.5, max_val=2)
    x = get_x()
    y = a * x ** n
    return x, y, r'$y = a x ^ n$', ('exponential', a, n)

def logarithmic_data():
    a, b, = random_params(2, min_val=0.5, max_val=2)
    x = get_x()
    y = a * np.log(x + b)
    return x, y, r'$y = a \log(x + b)$', ('logarithm', a, b)

SUPER_EASY_DATA = [
    line_data,
    # parable_data,
    sqrt_data,
    # exponential_data,
    logarithmic_data,
]

In [13]:
from collections import namedtuple

Peak = namedtuple('Peak', ['pos', 'amp', 'width'])

def gausian(x, pos, amp, width):
    return amp * np.exp(- (x - pos) ** 2 / (2 * width ** 2))

def multi_peak(x, *peaks):
    return np.sum(gausian(x, *peak) for peak in peaks)

def multi_peak_data():
    n_gausian = np.random.choice([1, 2, 3, 4])
    centers = random_params(n_gausian, min_val=2, max_val=18)
    amplitudes = random_params(n_gausian, min_val=5, max_val=20)
    widths = random_params(n_gausian, min_val=0.1, max_val=0.8)
    peaks = [
        Peak(c, a, w)
        for c, a, w in zip(centers, amplitudes, widths)
    ]
    x = get_x()
    y = multi_peak(x, *peaks)
    return x, y, '{} gausians'.format(n_gausian), ('multi peak', *[p._asdict() for p in peaks])

In [14]:
def easy_data():
    d1, d2 = np.random.choice(SUPER_EASY_DATA, 2, replace=False)
    x1, y1, label1, params1 = d1()
    x2, y2, label2, params2 = d2()
    return (
        x1,
        y1 + y2,
        '\n'.join([label1, label2]),
        (params1, params2)
    )

EASY_DATA = [
    easy_data
    for _ in range(20)
]

In [15]:
def hard_data():
    d1 = np.random.choice(SUPER_EASY_DATA)
    x1, y1, label1, params1 = d1()
    x2, y2, label2, params2 = multi_peak_data()
    return (
        x1,
        y1 + y2,
        'Gotta figure this one out',  # '\n'.join([label1, label2]),
        (params1, params2)
    )

HARD_DATA = [
    hard_data
    for _ in range(20)
]

In [16]:
import random
import numpy
import os

Substance = namedtuple('Substance', ['name', 'bp'])

WATER = Substance('Agua', 273.15 + 100)
SUBSTANCES = [
    Substance('Metanol',            273.15 + 65),
    Substance('Ciclopentanol',      273.15 + 140),
    Substance('Ciclohexanol',       273.15 + 161),
    Substance('Alcohol cinamílico', 273.15 + 257),
    Substance('Glicerol',           273.15 + 290),
]
ROOM_TEMP = 273.15 + 20


def transition(temp, center, mass, width=5):
    return mass * (numpy.tanh(- (temp - center) / width) + 1) / 2


def artificial_tga(temp, components):
    tga = numpy.zeros_like(temp)
    for subs, mass in components:
        tga += transition(temp, subs.bp, mass)
    return tga


def get_rand_solution(num_components, include_water=True):
    components = list(zip(
        random.sample(SUBSTANCES, num_components),
        numpy.round(
            numpy.random.uniform(0, 5, size=num_components),
            2
        ),
    ))
    if include_water:
        components.append((
            WATER,
            numpy.round(numpy.random.uniform(0, 2), 2)
        ))
    return list(components)


def generate_tga_data(path, components, points=100000, noise=0.01):
    solution = get_rand_solution(components)
    temp = numpy.linspace(ROOM_TEMP, 600, points)
    tga = artificial_tga(temp, solution)
    tga_exp = tga + numpy.random.normal(0, noise, size=points)
    numpy.savetxt(
        path + '.txt.gz',
        numpy.array([temp, tga_exp]).T,
        header='T[K]  m[g]'
    )
    return [{'component': c._asdict(), 'mass': w} for c, w in solution]

In [17]:
for guy in GUYS:
    guy_dir = os.path.join(PARTIAL_DIR, guy)
    create_missing_dirs(guy_dir)

    all_plots = (
        [np.random.choice(EASY_PLOTS)] +
        [np.random.choice(HARD_PLOTS)] +
        [np.random.choice(HARDER_PLOTS)]
    )

    pp = PdfPages(os.path.join(guy_dir, 'gráficos.pdf'))
    for p in all_plots:
        plt.figure()
        p()
        pp.savefig()
        plt.close()
    pp.close()

    solutions = []
    all_data = (
        [np.random.choice(EASY_DATA)] +
        [np.random.choice(HARD_DATA)] +
        [np.random.choice(HARD_DATA)]
    )
    for i, d in enumerate(all_data):
        x, y, label, conditions = d()
        np.savetxt(
            os.path.join(guy_dir, 'data{:02}.txt'.format(i)),
            np.array([x, y]).T,
            header=label,
        )
        solutions.append(conditions)
    
    solutions.extend([
            generate_tga_data(os.path.join(guy_dir, 'TGA{:2}'.format(0)), 2),
            generate_tga_data(os.path.join(guy_dir, 'TGA{:2}'.format(1)), 4)
    ])

    guy_solutions = os.path.join(SOLUTIONS_DIR, '{}.json'.format(guy))
    with open(guy_solutions, 'w') as sol:
        json.dump(solutions, sol, indent=4)
    
    print('done with', guy)


done with Federico
done with Sebastian Alejandro
done with Fernando
done with Ivan Dario
done with Sebastian
done with Christian David
done with Sergio Alejandro
done with Juan Camilo
done with Angel Santiago
done with Anngie Carolina
done with Cristian Elias
done with Juan Sebastian
done with Daniela
done with Maria Victoria
done with Laura Victoria
done with Maria Camila
done with Oscar (Bolaños)

In [ ]: