from IPython.core.display import HTML
HTML("<style>.container { width:100% !important; }</style>")
Caso: Función sencilla sin argumentos y ningún valor de retorno
WrapperFunciones/src/hola_mundo/ejemplo1.cpp
#include <iostream>
#include <string>
#include <boost/python.hpp>
void HolaMundo() {
std::cout << "Hola Mundo" << std::endl;
}
BOOST_PYTHON_MODULE(ejemplo1)
{
boost::python::def("hola_mundo", HolaMundo,"Funcion que muestra nuestro primer saludo");
}
Código para compilar:
g++ -I/usr/include/python3.4m -c -std=c++14 -fPIC -o ejemplo1.o ejemplo1.cpp
g++ -shared -lboost_python3 -o "ejemplo1.so" ./ejemplo1.o
Caso: Tenemos una función que multiplica un factor a cada elemento del vector:
WrapperFunciones/src/vector/multiplicacion.cpp
#include <vector>
#include <algorithm>
#include "multiplicacion.h"
std::vector<double> Multiplicacion(const std::vector<double> &datos, double factor) {
std::vector<double> datos_transformados(datos);
std::transform(datos_transformados.begin(), datos_transformados.end(),
datos_transformados.begin(),
[&factor](auto a) {return (a*factor);});
return datos_transformados;
}
WrapperFunciones/src/vector/ejemplo7.cpp
#include <iostream>
#include <boost/python.hpp>
#include "boost/python/stl_iterator.hpp"
#include "boost/python/numeric.hpp"
#include <boost/assign/std/vector.hpp>
#include "multiplicacion.h"
namespace py = boost::python;
py::numeric::array wrapper_multiplicacion(const py::numeric::array& datos, double factor = 1) {
// numpy -> std::vector
py::stl_input_iterator<double> begin(datos), end;
std::vector<double> datos_transformar(begin, end);
// LLamada a la función
auto nuevos_datos = Multiplicacion(datos_transformar, factor);
// std::vector -> numpy
py::list resultado;
for (auto const &i : nuevos_datos)
resultado.append(i);
return py::numeric::array(resultado);
}
BOOST_PYTHON_FUNCTION_OVERLOADS(args_por_defecto_multiplicacion, wrapper_multiplicacion, 1, 2);
BOOST_PYTHON_MODULE(ejemplo7)
{
py::numeric::array::set_module_and_type("numpy", "ndarray");
py::def("wrapper_multiplicacion", wrapper_multiplicacion,args_por_defecto_multiplicacion());
}
import numpy as np
factor_multiplicacion=1.5
numero_de_elementos=5
vector = np.arange(numero_de_elementos,dtype=np.float64)
vector
array([ 0., 1., 2., 3., 4.])
import ejemplo7
ejemplo7.wrapper_multiplicacion(vector,factor_multiplicacion)
array([ 0. , 1.5, 3. , 4.5, 6. ])
C++ | Python | Notas |
---|---|---|
std::vector< double > | numpy | En realidad cualquier clase iterable que se pueda construir por medio de iteradores y que la clase que contenga, tenga definido el constructor copia |
boost::posix_time::time_duration | datetime.timedelta | |
boost::gregorian::date | datetime.date | |
boost::posix_time::ptime | datetime | |
boost::local_time::local_date_time | datetime | Por ahora, únicamente reconoce la zona horaria UTC |
Ejemplos: std::vector a numpy y viceversa
WrapperFunciones/src/vector/ejemplo8.cpp
#include <iostream>
#include <boost/python.hpp>
#include "multiplicacion.h"
#include "../../../WrapperClases/convertidores/include/envoltorio_objetos_iterables.h" // Donde está definido el conversor
namespace py = boost::python;
BOOST_PYTHON_FUNCTION_OVERLOADS(args_por_defecto_multiplicacion, multiplicacion, 1, 2);
BOOST_PYTHON_MODULE(ejemplo8){
convertidores::RegistrarObjetosIterables<std::vector<double>>(); // Se registra
py::def("wrapper_multiplicacion", Multiplicacion);
}
import ejemplo8
ejemplo8.wrapper_multiplicacion(vector,factor_multiplicacion)
array([ 0. , 1.5, 3. , 4.5, 6. ])
Caso1 : Vamos a extender la clase Serie de una librería externa
WrapperClases/utiles/include/Serie.h
class Serie: private std::vector<double> {
typedef std::vector<double> vector;
public:
Serie();
Serie(const std::vector<double> &serie, const boost::posix_time::ptime &fecha_inicio,
const boost::posix_time::time_duration &resolucion = boost::posix_time::seconds(3600),
std::string descripcion = "");
boost::posix_time::ptime UltimaFechaValida() const;
boost::posix_time::ptime PrimeraFecha() const;
Serie Copiar() const;
const std::vector<double>& GetValores() const;
std::string GetDescripcion() const;
void SetDescripcion(std::string descripcion);
boost::posix_time::time_duration GetResolucion() const;
bool operator==(Serie const& otra_serie) const;
using vector::operator[];
using vector::begin;
using vector::end;
using vector::size;
using vector::iterator;
private:
boost::posix_time::ptime fecha_inicio;
boost::posix_time::time_duration resolucion;
std::string descripcion;
};
Serie operator+(const Serie& serie, double valor);
Serie operator-(const Serie& serie, double valor);
std::ostream &operator<<(std::ostream &os, const Serie &serie);
WrapperClases/wrapper_utiles/wrapper_utiles/src/serie.cpp
py::class_<utiles::Serie>("Serie",
py::init<std::vector<double>,boost::posix_time::ptime const&,py::optional<boost::posix_time::time_duration,std::string>>
(( py::arg("datos"),
py::arg("fecha_inicio"),
py::arg("resolucion")="3600",
py::arg("descripcion")=""
), "Constructor de la serie") )
.def("ultima_fecha_valida",&utiles::Serie::UltimaFechaValida) // boost::posix_time::ptime UltimaFechaValida() const;
.def("primera_fecha",&utiles::Serie::PrimeraFecha) // boost::posix_time::ptime PrimeraFecha() const;
.def_readonly("resolucion",&utiles::Serie::GetResolucion) // boost::posix_time::time_duration GetResolucion() const;
.add_property("descripcion",&utiles::Serie::GetDescripcion,&utiles::Serie::SetDescripcion) // std::string GetDescripcion() const, void SetDescripcion(std::string descripcion);
.def("valores", &utiles::Serie::GetValores,py::return_value_policy<py::copy_const_reference>()) //const std::vector<double> &GetValores() const;
.def(str(py::self)) // std::ostream &operator<<(std::ostream &os, const Serie &serie);
.def(repr(py::self)) // std::ostream &operator<<(std::ostream &os, const Serie &serie);
.def(py::self + double()) // Serie operator+(const Serie& serie, double valor);
.def(py::self - double()) // Serie operator-(const Serie& serie, double valor);
.def("__eq__",&utiles::Serie::operator==) // bool operator==(Serie const& otra_serie) const;
.def("__iter__",py::iterator<utiles::Serie>()) // using vector::iterator;
.def("__getitem__",&GetItemSerie) // using vector::operator[];
.def("__getitem__",&GetItemSliceSerie); // using vector::begin; using vector::end;
double GetItemSerie(const utiles::Serie& self, int index) {
if (index < 0)
index += static_cast<int>(self.size());
if (index < 0 || static_cast<int>(self.size()) <= index)
throw std::out_of_range("Fuera de rango");
return self[index];
}
py::numeric::array GetItemSliceSerie(const utiles::Serie& self,
py::slice slice) {
py::list resultado;
py::slice::range<std::vector<double>::const_iterator> rango;
try {
rango = slice.get_indices(self.begin(), self.end());
} catch (std::invalid_argument&) {
return py::numeric::array(resultado);
}
for (; rango.start != rango.stop; std::advance(rango.start, rango.step)) {
resultado.append(*rango.start);
}
resultado.append(*rango.start); //Ultimo elemento
return py::numeric::array(resultado);
}
Leeremos y jugaremos con los datos de observación de demanda y temperatura de de un estado de new york
# Añadimos al PYTHONPATH donde se ha compilado el módulo
# Desde setup.py
sys.path.append('../WrapperClases/build/lib.linux-x86_64-3.4')
# Desde eclipse
#sys.path.append('../WrapperClases/wrapper_utiles/wrapper_clases')
import wrapper_utiles as wu
import wrapper_utiles as wu
def plot(self):
indice=pd.date_range(self.primera_fecha(),self.ultima_fecha_valida(), freq='H')
data=dict(indice=indice,indice_text=[x.strftime("%Y-%m-%d %H") for x in indice],valores=self.valores())
source = ColumnDataSource(data)
TOOLS="pan,xwheel_zoom,box_zoom,reset,hover"
figura = Figure(plot_width=1000, plot_height=500, tools=TOOLS, x_axis_type='datetime', toolbar_location="left",title=self.descripcion)
figura.line('indice', 'valores',source=source, color="Black",legend=self.descripcion)
hover = figura.select(dict(type=HoverTool))
hover.tooltips = [
("(x,valor)", "(@indice_text, $y{1.11})"),
]
show(figura)
wu.serie.Serie.plot=plot
parse = lambda x,y: dt.datetime.strptime(x+" "+str(int(y)-1), '%Y%m%d %H')
datos=pd.read_csv('datos_new_england.txt',parse_dates=[['fecha','hora']],date_parser=parse,index_col='fecha_hora',dtype={'observacion':np.float,'temperatura':np.float})
valores=datos.temperatura.values
fecha_inicial=datos.index[0]
resolucion=dt.timedelta(seconds=3600)
serie=wu.serie.Serie(valores,fecha_inicial,resolucion,"Serie Temperatura")
serie.plot()
serie
Valores: [ 8.69, 8.01, 6.89, 6.22, 5.66, ... 6.76, 6.05, 6.05, 5.34, 5.20, ] Num.Valores: 744; Fecha Inicial: 2011-Dec-01 00:00:00; Resolucion: 01:00:00 Descripcion: Serie Temperatura
print ("Ultimos valores: {}".format(serie[-5:]))
Ultimos valores: [ 6.760993 6.050135 6.050135 5.344739 5.200192]
serie_copiada=serie.copiar()
serie_copiada == serie
True
print ("Serie Original:\n{}\nSerie Sumanda:\n{}".format(serie,serie+10))
Serie Original: Valores: [ 8.69, 8.01, 6.89, 6.22, 5.66, ... 6.76, 6.05, 6.05, 5.34, 5.20, ] Num.Valores: 744; Fecha Inicial: 2011-Dec-01 00:00:00; Resolucion: 01:00:00 Descripcion: Serie Temperatura Serie Sumanda: Valores: [ 18.69, 18.01, 16.89, 16.22, 15.66, ... 16.76, 16.05, 16.05, 15.34, 15.20, ] Num.Valores: 744; Fecha Inicial: 2011-Dec-01 00:00:00; Resolucion: 01:00:00 Descripcion: Serie Temperatura
Cuando hemos envuelto la clase utiles::Serie de la librería utiles, implicitamente boost.python nos ha registrado la clase, por lo que tenemos otra clase más registrada para envolver otros métodos de la librería utiles que tenga como argumento la clase utiles::Serie
WrapperClases/utiles/include/matematicas/convolucionar.h
#include <vector>
#include "../Serie.h"
namespace utiles {
namespace matematicas {
utiles::Serie ConvolucionarSerie(const utiles::Serie &datos,const std::vector<double> &filtro);
}
}
/home/pedro/repositorios_git/MeetUpMadrid/WrapperClases/wrapper_utiles/wrapper_utiles/src/matematicas/convolucionar.cpp
#include "../../include/matematicas/convolucionar.h"
#include <boost/python.hpp>
#include <include/matematicas/convolucionar.h>
namespace py = boost::python;
void wrapper_utiles::matematicas::exportar_convolucionar(){
// Nos provee el espacio de nombre
py::object modulo_convolucionar(py::handle<>(py::borrowed(PyImport_AddModule("wrapper_utiles.matematicas"))));
py::scope().attr("matematicas") = modulo_convolucionar;
py::scope util_scope = modulo_convolucionar;
py::def("convolucionar", &utiles::matematicas::ConvolucionarSerie,
"Convolucionar la serie de datos, primer elemento con el filtro, segundo elemento");
}
Con los mismos datos anteriores, aplicaremos un filtro paso alto a la serie
filtro_paso_alto = np.array([ 0.12940952, 0.22414387, -0.8365163 , 0.48296291])
serie_conv = wu.matematicas.convolucionar(serie,filtro_paso_alto)
serie_conv.plot()