Public
Edited
Jun 9, 2024
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
{
const num_days = regionsWithoutService_daily_peak_total.length;
const num_days_above_threshold = regionsWithoutService_daily_peak_total.filter(d => d.totalClientsWithoutService >= service_threshold).length;
const pct_days_above_threshold = num_days_above_threshold / num_days;
return htl.html`
Hay <strong>${num_days_above_threshold} de ${num_days} dias (${pct_days_above_threshold.toLocaleString(undefined,{style: 'percent', minimumFractionDigits:0})})</strong> con al menos <strong>${service_threshold.toLocaleString("en-US")} clientes</strong> tolerando apagones
`
}
Insert cell
{
const dataWidth = width;
const marginLeft = 50;
const marginRight = 50;
return Plot.plot({
width: dataWidth + marginLeft + marginRight,
marginLeft,
marginRight,
marks: [
Plot.rectY(clientes_afectados_por_dia,
{
x: 'marca_fecha_presentada',
y: "total_clientes_afectados",
fill: d => (d.total_clientes_afectados >= service_threshold) ? 'red' : 'black',
fillOpacity: d => (d.total_clientes_afectados >= service_threshold) ? .5 : 1,
tip: true,
interval: d3.utcDay}),
// Plot.rectY(regionsWithoutService_daily_peak,
// { filter: d => (d.type === 'totals') & (d.totalClientsWithoutService >= service_threshold),
// x: d => new Date(d.timestamp_date_string),
// y: "totalClientsWithoutService",
// fill: 'red', fillOpacity: 0.5,
// // stroke: 'red', strokeOpacity: 1.0,
// tip: true,
// interval: d3.utcDay}),
Plot.ruleY([0])
]
})
}
Insert cell
Insert cell
Insert cell
Insert cell
{
let thedata = clientes_afectados_por_dia
.map(d => Object({
fecha: d3.utcFormat('%Y-%m-%d')(d.marca_fecha_presentada),
total_clientes_afectados: d.total_clientes_afectados,
}))

return aq.from(thedata).view(1000)
}
Insert cell
Plot.plot({
width,
marks: [
Plot.ruleY([0]),
Plot.lineY(saidi_minutos, {
x: "marca_dia_presentada", y: "saidi_minutos", marker: true, tip: true})
]
})
Insert cell
Insert cell
Insert cell
{
let thedata = clientes_afectados_por_region_por_dia
.filter(d => d.region === region_ver)
.map(d => Object({
fecha: d3.utcFormat('%Y-%m-%d')(d.marca_fecha_presentada),
total_clientes_afectados: d.total_clientes_sin_servicio,
}))

return aq.from(thedata).view(1000)
}
Insert cell
clientes_afectados_por_region_por_dia
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
d3.timeFormat('%Y-%m-%d')(new Date())
Insert cell
new Date().toDateString()//.slice(0,10)
Insert cell
Insert cell
width
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
daily_observations_chart = {

let thedata = clientes_energizados_daychosen;
let thedata_max = thedata[d3.maxIndex(thedata, d => d.total_clientes_sin_servicio)];

let themargins = {
marginLeft: 10,
marginRight: 0,
// marginTop: 200,
marginTop: 20,
marginBottom: 40,
}

let the_width = chosen_width

// Input params for dot_symbol_packing_X
let chart_width = the_width - themargins.marginLeft - themargins.marginRight;
// let num_points = thedata.length;
let num_points = 288;
const min_radius = 1;
const max_radius = 3;
// output
const chosen_radius = dot_symbol_packing_X('circle', chart_width, num_points, min_radius, max_radius);

// Input for rule width scaling
const rule_padding = .95;
const min_rule_width = 0;
// output
const fit_radius = chart_width / (num_points - 1) / 2;
const fit_rule_width = fit_radius*2;
const padded_rule_width = fit_rule_width * (1 - rule_padding);
const chosen_rule_width = Math.max(
min_rule_width,
Math.min(padded_rule_width, 1)
);


let theplot = Plot.plot({
caption: `Radius: ${(chosen_radius)} Rule width: ${chosen_rule_width} Chart width: ${chart_width}`,
style: "overflow: visible;",
...themargins,
// marginLeft: 80,
width: the_width,
// width: 700,
height: 600,
// marginTop: 200,
x: {
domain: [fecha_picker, new Date(fecha_picker.getTime() + 1439*60000)],
},
y: {
domain: [0,100e3],
// ticks: 5,
// tickFormat: '%'
},
r: {type: 'linear'},
marks: [
Plot.axisX({
fontFamily: 'Inter, sans-serif',
}),
Plot.axisY({
rotate: -90,
textAnchor: 'middle',
tickFormat: d => (d/1e3).toString() + ' mil',
label: null,
fontSize: 12,
tickSize: 0,
fontFamily: 'Inter, sans-serif',
// ticks: 5,
ticks: [25e3, 50e3, 75e3, 100e3],
}),
Plot.gridY({
strokeDasharray: "2",
interval: 25e3, stroke: "black", strokeOpacity: 1}),
// Plot.areaY(
// thedata
// , {
// tip: false,
// x: "marca_hora_presentada",
// y1: d => d["total_clientes_sin_servicio"] >= service_threshold ? 0 : null,
// y2: d => d["total_clientes_sin_servicio"] >= service_threshold ? d["total_clientes_sin_servicio"] : null,
// fill: 'red', fillOpacity: 0.5,
// curve: 'step-after'
// //stroke: 'red', strokeOpacity: 1,
// }),
Plot.ruleX(thedata, {
x: 'marca_hora_presentada', y: d => d.total_clientes_sin_servicio !== 0 ? d.total_clientes_sin_servicio : null,
// strokeWidth: d => .2,
strokeWidth: chosen_rule_width,
}),
Plot.ruleX(thedata, {
x: 'marca_hora_presentada',
y1: d => d.total_clientes_sin_servicio !== 0 ? Math.max(0, d.total_clientes_sin_servicio - stroke_length) : null,
y2: d => d.total_clientes_sin_servicio !== 0 ? d.total_clientes_sin_servicio : null,
// strokeWidth: d => .2,
strokeWidth: 1,
stroke: 'black',
strokeLinecap: 'square',
}),
show_line ? Plot.line(
thedata
, {
tip: false,
x: "marca_hora_presentada", y:"total_clientes_sin_servicio",
stroke: 'darkblue',
strokeWidth: chosen_radius,
channels: {noService: "porcentaje_clientes_sin_servicio", hora: 'marca_hora_presentada'},
}) : null,
show_dot ? Plot.dot(
thedata
, {
tip: true,
x: "marca_hora_presentada", y:"total_clientes_sin_servicio",
stroke: null,
fill: 'black',
r: chosen_radius,
channels: {noService: "porcentaje_clientes_sin_servicio", hora: 'marca_hora_presentada'},
}) : null,
Plot.text([thedata_max], {
x: "marca_hora_presentada", y:"total_clientes_sin_servicio",
fill: 'black',
text: 'total_clientes_sin_servicio',
lineAnchor: 'bottom', dy: -8,
fontWeight: 'bold',
fontSize: 14,
fontFamily: 'B612 Mono',
}),

Plot.ruleY([0]),
// Plot.Tip()
]
})

return theplot
}
Insert cell
fecha_picker
Insert cell
Insert cell
Insert cell
clientes_energizados_daychosen[d3.maxIndex(clientes_energizados_daychosen, d => d.total_clientes_sin_servicio)]
Insert cell
Insert cell
estilos = htl.html`
<style>
@import url('https://fonts.googleapis.com/css2?family=B612+Mono:wght@400;700&family=Inter:wght@400;700&display=swap');
label {
font-family: 'Inter', sans-serif;
}
</style>
`
Insert cell
<style>
</style>
Insert cell
Insert cell
import lz77 from "lz77"
Insert cell
function dot_symbol_packing_X(symbol, chart_width, num_points, min_radius, max_radius) {

// Calculations
const fit_radius = chart_width / (num_points - 1) / 2;

// Outputs
const chosen_radius = Math.max(min_radius, Math.min(fit_radius, max_radius))
return chosen_radius * symbol_packing_scale_factors[symbol]
}
Insert cell
symbol_packing_scale_factors = Object({
'circle': 1, // default. 'r' maps to circle radius
'square': 1.1275, // 'r' maps to half a side length, after scaling
})
Insert cell
regions_service
Insert cell
Insert cell
regionsWithoutService_daily_peak_total = regionsWithoutService_daily_peak.filter(d => d.type === 'totals')
Insert cell
import {m4} from "@uwdata/m4-scalable-time-series-visualization"
Insert cell
// regions_service = d3.csvParse(datastring, d => {
// const dauto = d3.autoType(d);
// dauto['timestamp_saved'] = new Date(parsePRDate(dauto['timestamp_saved']) ) // idk whats up w this one
// dauto['timestamp'] = new Date(parsePRDate(dauto['timestamp']) ) // read as PR time (GMT-4). All set (?)
// dauto['region_name'] = dauto['region_name'] === null ? 'totals' : dauto['region_name']
// dauto['timestamp_date_string'] = dauto['timestamp'].toLocaleString(undefined, {
// "year": "numeric",
// "month": "2-digit",
// "day": "2-digit",
// timeZone: 'UTC', // so much pain
// })
// return dauto;
// })
Insert cell
// datastring = fetch("https://raw.githubusercontent.com/jzavala-gonzalez/scraping-luma/main/regionsWithoutService_historical.csv").then((response) => response.text())
Insert cell
clientes_energizados_por_region_dayago = clientes_energizados_por_region.filter(d => (d.marca_hora_presentada >= a_day_ago))
Insert cell
clientes_energizados_dayago = clientes_energizados.filter(d => (d.marca_hora_presentada >= a_day_ago))
Insert cell
clientes_energizados_daychosen = clientes_energizados.filter(d => d.marca_fecha_presentada.getTime() === fecha_picker.getTime())
Insert cell
clientes_energizados_daychosen_filled = clientes_energizados_filled.filter(d => d.marca_fecha_presentada.getTime() === fecha_picker.getTime())
Insert cell
clientes_energizados_filled.filter(d => d.marca_fecha_presentada === null)
Insert cell
clientes_energizados_daychosen
with listado_marca_hora_presentada as (
select
unnest(generate_series(
(marca_fecha_presentada::VARCHAR || ' 00:00:00')::DATETIME,
(marca_fecha_presentada::VARCHAR || ' 23:55:00')::DATETIME,
interval 5 minute
)) as marca_hora_presentada
from (from clientes_energizados_daychosen limit 1)
)


-- from listado_marca_hora_presentada
-- left join
-- clientes_energizados_daychosen
-- using (marca_hora_presentada)
-- order by marca_hora_presentada

from clientes_energizados_daychosen
Insert cell
clientes_energizados_daychosen
Type SQL, then Shift-Enter. Ctrl-space for more options.

Insert cell
latest_timestamp = d3.max(clientes_afectados_por_dia, d => d.marca_fecha_presentada)
Insert cell
latest_timestamp_date_string = latest_timestamp.toLocaleString(undefined, {
"year": "numeric",
"month": "2-digit",
"day": "2-digit",
timeZone: 'UTC', // so much pain
})
Insert cell
a_day_ago = new Date(getUTCTime(new Date(Date.now() - ms_per_hour*24)) - ms_per_hour*4)
Insert cell
ms_per_hour = 3_600_000
Insert cell
function range(n) { return Array.from(Array(n).keys()) }
Insert cell
function getUTCTime(date) {
let now_utc = Date.UTC(date.getUTCFullYear(), date.getUTCMonth(),
date.getUTCDate(), date.getUTCHours(),
date.getUTCMinutes(), date.getUTCSeconds());
return now_utc
}
Insert cell
function parseUTCDate(datestring) {
let date = new Date(datestring);
return getUTCTime(date);
}
Insert cell
function parsePRDate(datestring) {
let now_utc = parseUTCDate(datestring);
return now_utc - 3_600_000*4;
}
Insert cell
(parseUTCDate("07/21/2023 04:23 AM"))
Insert cell
new Date(parseUTCDate("07/21/2023 04:23 AM"))
Insert cell
new Date(parsePRDate("07/21/2023 04:23 AM"))
Insert cell
uniform_date(sample_date)
Insert cell
function uniform_date(date) {
return new Date(Date.UTC(0, 0,
1, date.getUTCHours(),
date.getUTCMinutes(), date.getUTCSeconds()) + 14_400_000*0)
}
Insert cell
sample_date = regions_service[0].timestamp
Insert cell
import {aq, op} from "@uwdata/arquero"
Insert cell
dbref
select *,
date_trunc('day', marca_hora_presentada) as marca_fecha_presentada,
from clientes_energizados
-- order by marca_hora_presentada
Insert cell
dbref
with clientes_energizados_copy as (
select *,
date_trunc('day', marca_hora_presentada) as marca_fecha_presentada,
from clientes_energizados
),
listado_marca_hora_presentada as (
select
unnest(generate_series(
(min(marca_fecha_presentada)::VARCHAR || ' 00:00:00')::DATETIME,
(max(marca_fecha_presentada)::VARCHAR || ' 23:55:00')::DATETIME,
interval 5 minute
)) as marca_hora_presentada
from clientes_energizados_copy
order by marca_hora_presentada
),

listado_marca_hora_y_fecha as (
select *, date_trunc('day', marca_hora_presentada) as marca_fecha_presentada,
from listado_marca_hora_presentada
)

select *,
marca_hora_presentada <= max(marca_hora_presentada) filter (marca_hora_accedida is not null) over () as dato_publicado,
case when not dato_publicado then false
when marca_hora_accedida is not null then true
else false end as dato_accedido,
from listado_marca_hora_y_fecha
left join
clientes_energizados_copy
using (marca_hora_presentada)
order by marca_hora_presentada
Insert cell
dbref
from clientes_energizados_por_region
Insert cell
// dbref = DuckDBClient.of({
// 'clientes_energizados': FileAttachment("clientes_energizados@4.parquet"),
// 'clientes_energizados_por_region': FileAttachment("clientes_energizados_por_region@4.parquet"),
// })
Insert cell
// dbempty = new DuckDBClient(db_instance)
dbempty = DuckDBClient.of({})
Insert cell
dbref = dbempty, (
await dbempty.query(`
create or replace table clientes_energizados_por_region as (
from read_parquet('${bucket_url}/datasets/clientes_energizados_por_region.parquet')
)
`)
),
(
await dbempty.query(`
create or replace table clientes_energizados as (
from read_parquet('${bucket_url}/datasets/clientes_energizados.parquet')
)
`)
),
dbempty
Insert cell
// loadquery = await dbref.query(`
// CREATE OR REPLACE TABLE regionsWithoutService AS (

// with initial_table as (
// SELECT
// strptime(timestamp_saved, '%m/%d/%Y %I:%M %p') as timestamp_saved,
// strptime(timestamp, '%m/%d/%Y %I:%M %p') as timestamp,
// type,
// region_name,
// totalClients,
// totalClientsWithoutService,
// totalClientsWithService,
// percentageClientsWithoutService,
// percentageClientsWithService
// FROM read_parquet('https://raw.githubusercontent.com/jzavala-gonzalez/scraping-luma/main/regionsWithoutService_historical.parquet')
// )

// select *,
// strftime(timestamp, '%m/%d/%Y') as timestamp_date_string,
// date_part('hour', timestamp) as timestamp_hour,
// from initial_table
// )
// `)
Insert cell
datadb
from clientes_afectados_por_dia
Insert cell
datadb
select
date_trunc('day', marca_hora_presentada) as marca_dia_presentada,
sum("total_clientes_sin_servicio")::FLOAT as cliente_observaciones_sin_servicio,
max(total_clientes) as "total_clientes_suplidos",
(cliente_observaciones_sin_servicio / total_clientes_suplidos) as saidi_observaciones,
round(saidi_observaciones * 5, 2) as saidi_minutos, -- 5 minutos per observacion
from clientes_energizados
-- where
-- date_part('year', marca_hora_presentada) = 2024
-- and date_part('month', marca_hora_presentada) <= 3
group by marca_dia_presentada
order by marca_dia_presentada
Insert cell
dailyquery = //loadquery,
(await dbref.query(`
CREATE OR REPLACE TABLE clientes_afectados_por_dia AS (

with extra_data as (
select *,
date_trunc('day', marca_hora_presentada) as marca_fecha_presentada,
( max(total_clientes_sin_servicio) over (partition by "marca_fecha_presentada", "region")) as the_worst
from clientes_energizados_por_region
),

only_the_worst as (

select distinct on (marca_fecha_presentada, region) * exclude the_worst
from extra_data
where total_clientes_sin_servicio = the_worst
order by marca_fecha_presentada, region, marca_hora_presentada -- NOTE: no deberiamos tener duplicados de marca_fecha en caso que mas de una hora presentada tengan el mismo valor
)

select
marca_fecha_presentada,
-- total_clientes_sin_servicio,
sum(total_clientes_sin_servicio)::INTEGER as total_clientes_afectados
from only_the_worst
group by marca_fecha_presentada
)
`))
Insert cell
datadb
from clientes_afectados_por_region_por_dia
Insert cell
query_clientes_afectados_por_region_por_dia = //loadquery,
(await dbref.query(`
CREATE OR REPLACE TABLE clientes_afectados_por_region_por_dia AS (

with extra_data as (
select *,
date_trunc('day', marca_hora_presentada) as marca_fecha_presentada,
( max(total_clientes_sin_servicio) over (partition by "marca_fecha_presentada", "region")) as the_worst
from clientes_energizados_por_region
),

only_the_worst as (

select distinct on (marca_fecha_presentada, region) * exclude the_worst
from extra_data
where total_clientes_sin_servicio = the_worst
order by marca_fecha_presentada, region, marca_hora_presentada -- NOTE: no deberiamos tener duplicados de marca_fecha en caso que mas de una hora presentada tengan el mismo valor
)

select
*
from only_the_worst
)
`))
Insert cell
datadb
from xmr_clientes_afectados_por_dia
Insert cell
xmr_clientes_afectados_por_dia_query = dailyquery,
(await dbref.query(`
CREATE OR REPLACE TABLE xmr_clientes_afectados_por_dia AS (

with sorted_data as (
select
marca_fecha_presentada as order_variable,
total_clientes_afectados as X
from clientes_afectados_por_dia
order by order_variable
),

xmr as (
select
order_variable,
X,
abs(X - lag(X) over ()) as mR,
mean(X) over () as X_mean,
from sorted_data
)

select
*,
mean(mR) over () as mR_mean,
case when (X_mean - 2.66*mR_mean) > 0 THEN (X_mean - 2.66*mR_mean) else 0 end as LPNL,
X_mean + 2.66*mR_mean as UPNL,
mR_mean*3.269 as URL,
from xmr
)
`))
Insert cell
datadb
from clientes_afectados_por_dia_crudo
Insert cell
dailyquery_crudo = //loadquery,
(await dbref.query(`
CREATE OR REPLACE TABLE clientes_afectados_por_dia_crudo AS (

with extra_data as (
select *,
date_trunc('day', marca_hora_presentada) as marca_fecha_presentada,
( max(total_clientes_sin_servicio) over (partition by "marca_fecha_presentada")) as the_worst
from clientes_energizados
),

only_the_worst as (

select distinct on (marca_fecha_presentada) * exclude the_worst
from extra_data
where total_clientes_sin_servicio = the_worst
order by marca_fecha_presentada, marca_hora_presentada
)

select
marca_fecha_presentada,
-- total_clientes_sin_servicio,
sum(total_clientes_sin_servicio)::INTEGER as total_clientes_afectados
from only_the_worst
group by marca_fecha_presentada
)
`))
Insert cell
bucket_url = 'https://numeros-luzpr.com' // smh
Insert cell
datadb = dailyquery_crudo, dailyquery, query_clientes_afectados_por_region_por_dia, xmr_clientes_afectados_por_dia_query, dbref
Insert cell
dbref
select 2 as two;
-- select *
-- from clientes_energizados_por_region
Insert cell
clientes_energizados_por_region = dbref.sql`from clientes_energizados_por_region;`
Insert cell
datadb
select *
from regionsWithoutService_daily_peak
Insert cell
regions_service[0]
Insert cell
duckdb = import(
"https://cdn.jsdelivr.net/npm/@duckdb/duckdb-wasm@1.28.1-dev106.0/+esm"
)
Insert cell
bundle = {
const bundles = duckdb.getJsDelivrBundles()
const duckdb_bundle = 'Auto';
if (duckdb_bundle === 'Auto') {
return duckdb.selectBundle(bundles)
} else {
const bun = bundles[duckdb_bundle]
bun['pthreadWorker'] = null;
return bun;
}
// return bundles['mvp']
}
Insert cell
async function makeDB() {
const logger = new duckdb.ConsoleLogger();
const worker = await duckdb.createWorker(bundle.mainWorker);
const db = new duckdb.AsyncDuckDB(logger, worker);
await db.instantiate(bundle.mainModule);
return db
}
Insert cell
db_instance = makeDB()
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more