Public
Edited
Jun 18, 2024
1 fork
Insert cell
Insert cell
Insert cell
Insert cell
partida_labels = Object({
'Inicial': 'Presupuesto del Ejecutivo',
'Revisado con aumento': 'Revisado\ncon aumento',
'Revisado con decremento': 'Revisado\ncon decremento',
'Parcialmente aprobado': 'Paricialmente\naprobado',
'Total': 'Presupuesto de la Junta',
})
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
labels_engligh = Object({
// #4
'Depto. de Educación': 'Department of Education',
'Ajuste financiamiento de instalaciones (AEE y AAA)': 'Financing Adjustment for PRASA',
'Servicios compartidos con el Dpto. de Seg. Pública': 'Shared Services with Public Safety Department',
'Adm. De Serv. Médicos': 'Emergency Medical Services Administration',
'Expiración de Reserva de Fondos Federales*': 'Expansion of Federal Funds Reserve*',
'Dpto. de Salud': 'Department of Health',
'Dpto. de la Policía': 'Police Administration',
'Adm. De Serv. Médicos*': 'Medical Services Administration*',
'Financ. Elecciones*': 'Elections Financing*',
'Asig. para transp. De la policía*': 'Transportation Assignment to Police*',
'Jta. de Planificación': 'Planning Board',
'Dpto. del Trabajo y Rec. Humanos': 'Department of Labor and Human Resources',
'Depto. de Recr. y Deportes': 'Department of Recreation and Sports',
'Corte General de Justicia': 'General Court of Justice',
'Ofic. Del Inspector General': 'Office of the Inspector General',
'Ofic. de Protec. y Def. Personas con Impedimentos': 'Office of Defense for People with Disabilities',
'Dpto. de Hacienda': 'Department of Treasury',
'Adm. de Servicios Generales': 'General Services Administration',
'Guardia Nacional': 'National Guard',
'Inst. de Estadísticas': 'Institute of Statistics',
'Ofic. para el Desarollo Socioec. y Comunit.': 'Office for Socioeconomic and Community Development',
'Ofic. del Contralor Electoral': 'Office of the Electoral Comptroller',
'Centro de Diabetes': 'Diabetes Center',
'Ofic. de Transf. de Rec. Humanos': 'Human Resources Transformation Office',
'Neg. de Invest. Especiales': 'Special Investigations',
'Ofic. del Procurador del Veterano': 'Veteran\'s Advocate Office',
'Comisión de Desarrollo Cooperativo': 'Autonomous Municipalities Development Commission',
'Aut. del Puerto de las Américas': 'Ports Authority',
'Junta de Retiro': 'Retirement Board',
// #3
'Reforma del servicio público*': 'Public Service Reform*',
'Facilitador de Educación Especial*': 'Special Education Facilitator*',
'Iniciativa Vida Plus*': 'Vida Plus Initiative*',
'Comisión Estatal de Elecciones': 'State Elections Commission',
'Subsidio para CDTs*': 'Subsidy for CDTs*',
'Dpto. de Estado': 'Department of State',
'Dpto. De Salud': 'Department of Health',
'Planes Territoriales Municipales*': 'Municipal Territorial Plans*',
'Junta de Planificación': 'Planning Board',
'Negociado de la Policia': 'Police Department',
'Ofic. del Def. del Pueblo': 'Office of the Citizen\'s Ombudsman',
'Ofic. De Transf. de Rec. Humanos': 'Human Resources Transformation Office',
'Administración de Familia y Niños': 'Family and Children Administration',
'Dpto. de Justicia': 'Deparment of Justice',
'Servicios de Salud Correccional': 'Correctional Health Services',
'Total': 'FOMB Budget',
// #1
'Contrib. a partidos políticos': 'Contribution to Political Parties',
'Dpto. de Educación': 'Department of Education',
'Matching FEMA*': 'FEMA Matching*',
'CIMVAS*': 'CIMYAS*',
'Centro Comprensivo de Cáncer': 'Comprehensive Cancer Center',
'DRNA - Reserva mantenimiento*': 'DNER - Maintenance reserve*',
'Centro Comprensivo de Cáncer*': 'Comprehensive Cancer Center*',
'Neg. de Bomberos': 'Fire Department',
'Dpto. De Seguridad Pública*': 'Public Safety Department*',
'Cuerpo de Emerg. Médicas': 'Emergency Medical Corps',
'DRNA*': 'DNER*', 'DRNA': 'DNER',
'OGP': 'OMB', 'OGP*': 'OMB*',

// #2
'Universidad de Puerto Rico*': 'University of Puerto Rico*',
'Municipios': 'Municipalities',
'Agenda de Administración Financiera*': 'Financial Management Agenda*',
'IDEAR - Dept. de Educación*': 'IDEAR - Department of Education*',
'Plan de Prevención de violencia y reconstrucción social*': 'Violence Prevention and Social Reconstruction Plan*',
'Dept. de Agricultura': 'Department of Agriculture',
'Financiación a entidades de salud*': 'Financing to Health Entities*',
'Análisis de Empleados - Dept. de Seguridad Pública*': 'Employee Analysis - Public Safety Department*',
'Dpto. de Corrección y Rehab.': 'Department of Corrections and Rehabilitation',
'Centros Residenciales para Menores bajo custodia del Gobierno*': 'Residential Centers for Minors in Government Custody*',
'Oficina de la Mujer': 'Office of the Women\'s Advocate',
'Liquidación de vacaciones*': 'Vacation Liquidation*',
'Pagos a Sin Fines de Lucro*': 'Payments to Non-Profit Organizations*',
'Inst. Ciencias Forenses': 'Forensic Sciences Institute',
'Secretariado del Dpto. de la Familia': 'Secretariat of the Department of Family',
'Dept. de Estado': 'State Department',
'Oficina del Gobernador': 'Office of the Governor',
})
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
htl.html`
<style>
figcaption {
max-width: 100%;
}
</style>
`
Insert cell
aq.from(comparativo_area_concepto).view(300)
Insert cell
comparativo_area_concepto = {
const group_cols = ['Nombre Area Observatorio', 'Nombre Concepto']

let thedata = aq.from(sabana)
.filter(d => d['Fondo'] === 'Subtotal Fondo General')
.filter(
d => (d['2025_version_01'] > 0) | (d['2025_version_02'] > 0)
)
.groupby(group_cols)
.rollup({
'2025_version_01': op.sum('2025_version_01'),
'2025_version_02': op.sum('2025_version_02'),
})
.derive({
'Variacion': d => (d['2025_version_02'] - d['2025_version_01']),
})
.derive({
'Variacion %': aq.escape(d => (d['2025_version_01'] !== 0n) ? (
Number(d['Variacion']) / Number(d['2025_version_01'])
) : null),
})
.orderby(group_cols)
.rename({
'Nombre Area Observatorio': 'area_observatorio',
'Nombre Concepto': 'nombre_concepto',
'Variacion': 'variacion_dol',
'Variacion %': 'variacion_pct',
})
.objects()

const cols_scale = ['variacion_dol', '2025_version_01', '2025_version_02'];
thedata.forEach(d => {
for (let c of cols_scale) {
d[c] = Number(d[c]) / 1e3 // miles a millones
}
})

return thedata
}
Insert cell
aq.from(comparativo_area_programa).view()
Insert cell
comparativo_area_programa = {
const group_cols = ['Nombre Area Observatorio', 'Nombre Programa']

let thedata = aq.from(sabana)
.filter(d => d['Fondo'] === 'Subtotal Fondo General')
.filter(
d => (d['2025_version_01'] > 0) | (d['2025_version_02'] > 0)
)
.groupby(group_cols)
.rollup({
'2025_version_01': op.sum('2025_version_01'),
'2025_version_02': op.sum('2025_version_02'),
})
.derive({
'Variacion': d => (d['2025_version_02'] - d['2025_version_01']),
})
.derive({
'Variacion %': aq.escape(d => (d['2025_version_01'] !== 0n) ? (
Number(d['Variacion']) / Number(d['2025_version_01'])
) : null),
})
.orderby(group_cols)
.rename({
'Nombre Area Observatorio': 'area_observatorio',
'Nombre Programa': 'nombre_programa',
'Variacion': 'variacion_dol',
'Variacion %': 'variacion_pct',
})
.objects()

const cols_scale = ['variacion_dol', '2025_version_01', '2025_version_02'];
thedata.forEach(d => {
for (let c of cols_scale) {
d[c] = Number(d[c]) / 1e3 // miles a millones
}
})

return thedata
}
Insert cell
aq.from(comparativo_area_descripcion)
.filter(d => d.area_observatorio === 'Educación')
.view()
Insert cell
comparativo_area_descripcion = {
const group_cols = ['Nombre Area Observatorio', 'DESCRIPCION']

let thedata = aq.from(sabana)
.filter(d => d['Fondo'] === 'Subtotal Fondo General')
.filter(
d => (d['2025_version_01'] > 0) | (d['2025_version_02'] > 0)
)
.groupby(group_cols)
.rollup({
'2025_version_01': op.sum('2025_version_01'),
'2025_version_02': op.sum('2025_version_02'),
})
.derive({
'Variacion': d => (d['2025_version_02'] - d['2025_version_01']),
})
.derive({
'Variacion %': aq.escape(d => (d['2025_version_01'] !== 0n) ? (
Number(d['Variacion']) / Number(d['2025_version_01'])
) : null),
})
.orderby(group_cols)
.rename({
'Nombre Area Observatorio': 'area_observatorio',
'DESCRIPCION': 'descripcion',
'Variacion': 'variacion_dol',
'Variacion %': 'variacion_pct',
})
.objects()

const cols_scale = ['variacion_dol', '2025_version_01', '2025_version_02'];
thedata.forEach(d => {
for (let c of cols_scale) {
d[c] = Number(d[c]) / 1e3 // miles a millones
}
})

return thedata
}
Insert cell
db
from sabana
Insert cell
aq.from(cambios_por_area)
.fold(['aumento','decremento'], {as: ['tipo_cambio', 'cantidad']})
.view()
Insert cell
80.23 / 396.23
Insert cell
aq.from(all_items)
.filter(d => d.area_observatorio === 'Educación')
.orderby('cantidad')
.view()
Insert cell
aq.from(cambios_por_area).view()
Insert cell
Insert cell
aq
Insert cell
Math.sign(0)
Insert cell
Insert cell
import {DuckDBClientKL} from "@kimmolinna/duckdbclient"
Insert cell
// db = DuckDBClientKL.of({
db = DuckDBClient.of({
sabana: FileAttachment("sabana_with_area.parquet")
})
Insert cell
comparativo_area = aq.from(comparativo_area_agencia)
.groupby('area_observatorio')
.rollup({
'2025_version_01': op.sum('2025_version_01'),
'2025_version_02': op.sum('2025_version_02'),
'variacion_dol': op.sum('variacion_dol'),
})
.derive({
'variacion_pct': d => d.variacion_dol / d['2025_version_01']
})
.orderby('variacion_dol')
.objects()
Insert cell
comparativo_area_agencia = {
let workbook = await FileAttachment('fondo_general_area_observatorio_agencia.xlsx').xlsx()
let data = workbook.sheet('Sheet1', {headers: true})
data = aq.from(data)
.rename({
'Nombre Area Observatorio': 'area_observatorio',
'Nombre Agencia': 'nombre',
'Variacion': 'variacion_dol',
'Variacion %': 'variacion_pct',
})
.derive({
'2025_version_01': d => d['2025_version_01'] / 1e3,
'2025_version_02': d => d['2025_version_02'] / 1e3,
'variacion_dol': d => d.variacion_dol / 1e3, // cambiar de miles a millones
})
.objects();
return data
}
Insert cell
comparativo_concepto = aq.from(comparativo_concepto_descripcion)
.groupby('nombre_concepto')
.rollup({
'2025_version_01': op.sum('2025_version_01'),
'2025_version_02': op.sum('2025_version_02'),
'variacion_dol': op.sum('variacion_dol'),
})
.derive({
'variacion_pct': d => d.variacion_dol / d['2025_version_01']
})
.orderby('variacion_dol')
.objects()
Insert cell
comparativo_concepto_descripcion = {
let workbook = await FileAttachment('fondo_general_concepto_descripcion.xlsx').xlsx()
let data = workbook.sheet('Sheet1', {headers: true})
data = aq.from(data)
.rename({
'Nombre Concepto': 'nombre_concepto',
'DESCRIPCION': 'descripcion',
'Variacion': 'variacion_dol',
'Variacion %': 'variacion_pct',
})
.derive({
'2025_version_01': d => d['2025_version_01'] / 1e3,
'2025_version_02': d => d['2025_version_02'] / 1e3,
'variacion_dol': d => d.variacion_dol / 1e3, // cambiar de miles a millones
})
.objects();
return data
}
Insert cell
workbook = FileAttachment("Analysis_FOMB_NoV_05212024@5.xlsx").xlsx()
Insert cell
workbook.sheetNames
Insert cell
denied_items = {
let data = workbook.sheet('denied', {
headers: true,
// range: "A1:J10"
})

data.forEach(d => {
d.status = 'denied';
d.label = d.nombre
if (d.bajo_custodia) {
d.label += '*'
}
})
return data
}
Insert cell
partially_approved_items = {
let data = workbook.sheet('partially_approved', {
headers: true,
// range: "A1:J10"
})

data.forEach(d => {
d.status = 'partially_approved';
d.label = d.nombre
if (d.bajo_custodia) {
d.label += '*'
}
})
return data
}
Insert cell
approved_items = {
let data = workbook.sheet('approved', {
headers: true,
// range: "A1:J10"
})

data.forEach(d => {
d.status = 'approved';
d.label = d.nombre
if (d.bajo_custodia) {
d.label += '*'
}
})
return data
}
Insert cell
readjusted_items = {
let data = workbook.sheet('reajustes', {
headers: true,
// range: "A1:J10"
})

data.forEach(d => {
d.status = 'readjusted';
d.label = d.nombre
if (d.bajo_custodia) {
d.label += '*'
}
})
return data
}
Insert cell
all_items = [
...denied_items,
...partially_approved_items,
...approved_items,
...readjusted_items,
]
Insert cell
fmt = n => d3.format(",d")(n)
Insert cell
plotLabel = (data, dy) =>
Plot.text(data, {
x: "partida",
y: "accu",
dy: dy,
//frameAnchor: anchor,
fontWeight: "bold",
text: d => d3.format("$,.1f")(d.accu)+'M',
fontSize: 16, // stroke: 'none', fill: 'white',
fontVariant: 'tabular-nums',
fontFamily: 'jaf-bernino-sans',
})
Insert cell
waterfall = function f(numbers,accu_start, name) {
let last = 0, accu = accu_start;
let waterfall = numbers.map((d, i) => {
last = accu;
accu += d.profit;
return {
[name]: d[name],
nextDay: i < (numbers.length - 1) ? numbers[i + 1][name] : "Total",
prior: last,
accu: accu,
profit: d.profit
};
});

waterfall = [
{
[name]: "Inicial",
nextDay: numbers[0][name],
prior: accu_start,
accu: accu_start,
profit: 0
},
...waterfall,
{
[name]: "Total",
nextDay: null,
prior: accu_start,
accu: accu,
profit: 0
}
]
// waterfall.push({
// partida: "Total",
// nextDay: null,
// prior: accu_start,
// accu: accu,
// profit: 0
// });
return waterfall;
}
Insert cell
waterfall(
fomb_adjustment.filter(d => (d.partida !== 'Partial') | show_partial)
.map(d => Object({...d, profit: d.cantidad})), 13062.30 ,'partida')
Insert cell
fomb_adjustment = [
{partida: 'En cumplimiento', cantidad: 130.70},
{partida: 'Revisado con aumento', cantidad: 291.09},
{partida: 'Revisado con decremento', cantidad: -20.72},
{partida: 'Denegado', cantidad: -396.96},
{partida: 'Parcialmente aprobado', cantidad: -118.90, partial: true},
]
Insert cell
Insert cell
Insert cell
estilos = htl.html`
<style>
h1 {
font-family: "jaf-bernino-sans-comp", sans-serif;
font-weight: 700;
font-style: normal;
}

h2 {
font-family: "jaf-bernino-sans-narrow", sans-serif;
font-weight: 700;
font-style: normal;
}

span {
font-family: "jaf-bernino-sans", sans-serif;
font-weight: 400;
font-style: normal;
}

span[class$="-swatch"] {
font-size: 16px;
}
</style>
`
Insert cell
import { EA_COLORES_MAP} from "22dc6182207c44c8"
Insert cell
import {aq, op} from '@uwdata/arquero';
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more