Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
_site
.git
.jekyll-cache
.jekyll-metadata
.sass-cache
.bundle
vendor
Gemfile.lock
.DS_Store
37 changes: 37 additions & 0 deletions .github/workflows/update-citations.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: Update Google Scholar citations

on:
schedule:
- cron: "0 3 * * 1" # weekly, Monday 03:00 UTC
workflow_dispatch: # allow manual runs

permissions:
contents: write

jobs:
update-citations:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: "3.11"

- name: Install dependencies
run: pip install -r requirements.txt

- name: Fetch citation counts
run: python bin/update_scholar_citations.py

- name: Commit updated citations
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add _data/citations.yml
if git diff --staged --quiet; then
echo "No citation changes."
else
git commit -m "chore: update Google Scholar citations"
git push
fi
19 changes: 0 additions & 19 deletions .travis.yml

This file was deleted.

21 changes: 21 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Development image for building the al-folio site on a pinned Ruby.
#
# Rationale: newer host Rubies (e.g. Ruby 4.0) dropped several libraries from
# the default gem set (ostruct, bigdecimal, ...), which breaks the legacy plugin
# chain. Pinning Ruby 3.2 here gives a reliable local build/serve regardless of
# the host toolchain. Deployment still happens via GitHub Actions.
FROM ruby:3.2

RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
&& rm -rf /var/lib/apt/lists/*

WORKDIR /srv/jekyll

COPY Gemfile ./
RUN gem install bundler && bundle install

EXPOSE 4000 35729

CMD ["bundle", "exec", "jekyll", "serve", \
"--host", "0.0.0.0", "--livereload", "--force_polling", "--watch"]
12 changes: 12 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
source 'https://rubygems.org'

group :jekyll_plugins do
gem 'jekyll'
gem 'jekyll-email-protect'
gem 'jekyll-feed'
gem 'jekyll-github-metadata'
gem 'jekyll-jupyter-notebook'
gem 'jekyll-paginate-v2'
gem 'jekyll-scholar'
gem 'jekyll-twitter-plugin'
Expand All @@ -14,3 +16,13 @@ group :jekyll_plugins do
gem 'unicode_utils'
gem 'webrick'
end

# Ruby 4.0 removed these from the default gem set. Listing them explicitly keeps
# the build working on Ruby 4.x while remaining harmless on Ruby 3.2 (where they
# are still available as gems). The Docker image pins Ruby 3.2, so this is only
# load-bearing for local builds on a newer host Ruby.
gem 'ostruct'
gem 'bigdecimal'
gem 'csv'
gem 'logger'
gem 'faraday-retry'
8 changes: 8 additions & 0 deletions _bibliography/papers.bib
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ @inproceedings{GohLWSB21Understanding
address = {Virtual Event / Milan, Italy},
html = {https://doi.org/10.1109/ICPR48806.2021.9413242},
doi = {10.1109/ICPR48806.2021.9413242},
google_scholar_id = {j3f4tGmQtD8C},
arxiv = {2004.10484},
abbr = {ICPR},
code = {https://github.com/garygsw/smooth-taylor},
Expand Down Expand Up @@ -59,6 +60,7 @@ @inproceedings{GohKZ18Twitter
address = {Singapore},
html = {https://doi.org/10.1109/ICDMW.2018.00097},
doi = {10.1109/ICDMW.2018.00097},
google_scholar_id = {4JMBOYKVnBMC},
abbr = {ICDM},
code = {https://github.com/garygsw/twitter-crowd-flow-prediction},
slides = {icdm2018/ICDMW2018_TCFP_Slides.pdf},
Expand All @@ -84,6 +86,7 @@ @inproceedings{OngGX15Sparsity
address = {Santa Clara, CA, USA},
html = {https://doi.org/10.1109/BigData.2015.7363995},
doi = {10.1109/BigData.2015.7363995},
google_scholar_id = {d1gkVwhDpl0C},
abbr = {IEEE BigData},
pdf = {ieeebigdata2015/IEEEBigData_SAIG_Paper.pdf}
}
Expand All @@ -105,6 +108,7 @@ @inproceedings{YangGJZA15Forecast
address = {Santa Clara, CA, USA},
html = {https://doi.org/10.1109/BigData.2015.7363994},
doi = {10.1109/BigData.2015.7363994},
google_scholar_id = {u-x6o8ySG0sC},
abbr = {IEEE BigData},
pdf = {ieeebigdata2015/IEEEBigData_ForecastFMCG-II_Paper.pdf}
}
Expand All @@ -126,6 +130,7 @@ @inproceedings{YangGXZA15Forecast
address = {Santa Clara, CA, USA},
html = {https://doi.org/10.1109/BigData.2015.7363993},
doi = {10.1109/BigData.2015.7363993},
google_scholar_id = {u5HHmVD_uO8C},
abbr = {IEEE BigData},
pdf = {ieeebigdata2015/IEEEBigData_ForecastFMCG-I_Paper.pdf}
}
Expand Down Expand Up @@ -159,6 +164,7 @@ @inproceedings{YangGJZ16Spatial
address = {Washington DC, USA},
html = {https://doi.org/10.1109/BigData.2016.7841052},
doi = {10.1109/BigData.2016.7841052},
google_scholar_id = {RHpTSmoSYBkC},
abbr = {IEEE BigData},
pdf = {ieeebigdata2016/IEEEBigData_SpatialQuadTree_Paper.pdf}
}
Expand Down Expand Up @@ -192,6 +198,7 @@ @inproceedings{YangGJZ16aForecast
address = {Washington DC, USA},
html = {https://doi.org/10.1109/BigData.2016.7841053},
doi = {10.1109/BigData.2016.7841053},
google_scholar_id = {_Qo2XoVZTnwC},
abbr = {IEEE BigData},
pdf = {ieeebigdata2015/IEEEBigData_ForecastFMCG-III_Paper.pdf}
}
Expand Down Expand Up @@ -224,6 +231,7 @@ @inproceedings{GohAZ16Optimizing
address = {Washington DC, USA},
html = {https://doi.org/10.1109/BigData.2016.7841042},
doi = {10.1109/BigData.2016.7841042},
google_scholar_id = {e5wmG9Sq2KIC},
abbr = {IEEE BigData},
pdf = {ieeebigdata2016/IEEEBigData_SentimentDOE_Paper.pdf}
}
4 changes: 4 additions & 0 deletions _config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,10 @@ enable_navbar_social:
# navbar on the about page
enable_project_categories: true # enables categorization of projects into # multiple categories
enable_medium_zoom: true # enables image zoom feature (as on medium.com)
enable_search: false # enables the ninja-keys command-palette site search
# Google Scholar citation badge on publications (count sourced from _data/citations.yml)
enable_publication_badges:
google_scholar: true

# -----------------------------------------------------------------------------
# Library versions
Expand Down
38 changes: 38 additions & 0 deletions _data/citations.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Google Scholar citation counts, keyed by "<scholar_userid>:<google_scholar_id>".
# Auto-refreshed by .github/workflows/update-citations.yml (bin/update_scholar_citations.py).
# Values below were seeded from the Google Scholar profile (user SuI63bsAAAAJ).
metadata:
last_updated: "2026-07-11"
papers:
"SuI63bsAAAAJ:j3f4tGmQtD8C":
title: Understanding Integrated Gradients with SmoothTaylor for Deep Neural Network Attribution
year: 2021
citations: 63
"SuI63bsAAAAJ:u-x6o8ySG0sC":
title: "Forecast UPC-level FMCG demand, Part II: Hierarchical Reconciliation"
year: 2015
citations: 19
"SuI63bsAAAAJ:4JMBOYKVnBMC":
title: Twitter-Informed Crowd Flow Prediction
year: 2018
citations: 16
"SuI63bsAAAAJ:d1gkVwhDpl0C":
title: Sparsity Adjusted Information Gain for Feature Selection in Sentiment Analysis
year: 2015
citations: 14
"SuI63bsAAAAJ:_Qo2XoVZTnwC":
title: "Forecast UPC-level FMCG demand, Part III: Grouped Reconciliation"
year: 2016
citations: 9
"SuI63bsAAAAJ:u5HHmVD_uO8C":
title: "Forecast UPC-level FMCG demand, Part I: Exploratory Analysis and Visualization"
year: 2015
citations: 9
"SuI63bsAAAAJ:RHpTSmoSYBkC":
title: "Spatial Data Dimension Reduction using Quadtree: A Case Study on Satellite-derived Solar Radiation"
year: 2016
citations: 5
"SuI63bsAAAAJ:e5wmG9Sq2KIC":
title: Optimizing Performance of Sentiment Analysis through Design of Experiments
year: 2016
citations: 3
14 changes: 8 additions & 6 deletions _data/cv.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@
end_date: Present
location: Singapore
description:
- Work across the full data function end to end &mdash; data engineering, analytics, data science, and AI transformation &mdash; supporting management decision-making and fundraising
- Architected <b>Data Warehouse 2.0</b> on <a href="https://www.databricks.com"><b>Databricks</b></a>, migrating to a governed native catalog with CDC streaming via <b>AWS DMS</b> and cutting data-platform costs by ~80%
- Designed a company-wide AI operating model &mdash; a git-based knowledge base plus a governed <b>MCP</b> layer connecting AI agents to live company data (Databricks, Jira, GitHub, Drive, Gmail, Mixpanel) &mdash; and rolled out Claude and GPT/Codex org-wide with read-only and PII-ringfencing guardrails
- Built a self-healing LLM pipeline that auto-classifies Databricks job failures, self-repairs or opens PRs, and closes Jira tickets, eliminating manual triage
- Deployed predictive models, including quantile-regression doctor-demand forecasting, an AI Fraud/Waste/Abuse detection engine across 14 risk categories, and B2B corporate segmentation
- Data lead for the Series C fundraise data room; delivered self-serve natural-language analytics via <a href="https://www.databricks.com/product/ai-bi/genie"><b>Databricks Genie</b></a> and an MCP-connected agent
- "<b>Business Impact</b>: Drove regional data science initiatives (SG/MY/PH/HK/ID), delivering 10&ndash;30% incremental revenue and operational cost savings."
- "<b>Data Architecture</b>: Architected <b>Data Warehouse 2.0</b> on <a href='https://www.databricks.com'>Databricks</a>; migrated to a governed native catalog with CDC streaming via <b>AWS DMS</b>, slashing data platform costs by 80%."
- "<b>Company-Wide AI Transformation</b>: Designed a git-based knowledge base and a governed <b>MCP</b> layer connecting AI agents to live company data (Databricks, Jira, GitHub, Drive, Gmail, Mixpanel)."
- "<b>AI Governance</b>: Implemented read-only guardrails, PII ringfencing, and infrastructure guardrails while rolling out Claude and GPT/Codex org-wide."
- "<b>AI Agent Automation</b>: Built a self-healing LLM pipeline that auto-classifies Databricks job failures, self-repairs/opens PRs, and manages Jira tickets, eliminating manual triage."
- "<b>Clinical GenAI</b>: Deployed patient-facing &ldquo;AI Triage&rdquo; and doctor-facing &ldquo;Clinical Support&rdquo; bots using LangChain/LangGraph on AWS AgentCore, validated via in-house eval datasets."
- "<b>Self-Serve Analytics</b>: Integrated Databricks Genie with an MCP-connected agent for natural-language querying, reducing ad-hoc data requests from non-technical teams."
- "<b>Predictive Modeling</b>: Deployed quantile regression for doctor demand forecasting, an AI Fraud/Waste/Abuse detection engine across 14 risk categories, and B2B corporate segmentation models."
- title: Data Scientist
subtitle: "<i><a href='https://www.sea.com/home'>Sea Ltd</a> &mdash; <a href='https://www.garena.sg'>Garena</a></i>"
year: 2021-2022
Expand Down
2 changes: 1 addition & 1 deletion _data/venues.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ ICDM:
url: http://icdm.bigke.org

SUTD:
url: https://www.sutd.edu.sg/
url: https://www.sutd.edu.sg/
14 changes: 11 additions & 3 deletions _includes/header.html
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,19 @@
{% endif %}
{% endif %}
{% endfor %}
{% if site.enable_search %}
<div class = "toggle-container">
<a id = "search-toggle" role="button" title="Search (⌘K / Ctrl-K)">
<i class="fas fa-search"></i>
</a>
</div>
{% endif %}
{% if site.enable_darkmode %}
<div class = "toggle-container">
<a id = "light-toggle">
<i class="fas fa-moon"></i>
<i class="fas fa-sun"></i>
<a id = "light-toggle" role="button" title="Cycle light / dark / system theme">
<i class="fas fa-sun" title="Switch to dark theme"></i>
<i class="fas fa-moon" title="Switch to system theme"></i>
<i class="fas fa-adjust" title="Switch to light theme"></i>
</a>
</div>
{% endif %}
Expand Down
18 changes: 18 additions & 0 deletions _includes/jupyter_notebook.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{%- comment -%}
Embed a Jupyter notebook.

Preferred (needs Jupyter installed + jekyll-jupyter-notebook, which is in the Gemfile):
{% jupyter_notebook "assets/jupyter/blog.ipynb" %}

Portable fallback (no local Jupyter toolchain) — embed pre-rendered notebook HTML
or an nbviewer page in an iframe:
{% include jupyter_notebook.html path="/assets/jupyter/demo.html" %}
{% include jupyter_notebook.html url="https://nbviewer.org/github/<user>/<repo>/blob/main/nb.ipynb" %}
{%- endcomment -%}
{%- if include.path -%}
{%- assign nb_src = include.path | relative_url -%}
{%- else -%}
{%- assign nb_src = include.url -%}
{%- endif -%}
<iframe class="jupyter-notebook-iframe" src="{{ nb_src }}" loading="lazy"
title="{{ include.title | default: 'Jupyter notebook' }}"></iframe>
44 changes: 44 additions & 0 deletions _includes/scripts/diagrams.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<!--
Rich-content renderers, loaded only on pages that opt in via front matter:
chart_js: true -> Chart.js (<canvas> + new Chart(...))
plotly: true -> Plotly.js (<div> + Plotly.newPlot(...))
mermaid: true -> Mermaid diagrams (<div class="mermaid">...)
pseudocode: true -> pseudocode.js (<pre class="pseudocode">...) [needs KaTeX]
tikzjax: true -> TikZJax (<script type="text/tikz">...)
-->
{% if page.chart_js %}
<script src="https://cdn.jsdelivr.net/npm/chart.js@4"></script>
{% endif %}

{% if page.plotly %}
<script src="https://cdn.plot.ly/plotly-2.35.2.min.js" charset="utf-8"></script>
{% endif %}

{% if page.mermaid %}
<script src="https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js"></script>
<script>
mermaid.initialize({
startOnLoad: true,
theme: document.documentElement.getAttribute("data-theme") === "dark" ? "dark" : "default",
});
</script>
{% endif %}

{% if page.pseudocode %}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/katex.min.css">
<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/dist/katex.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/build/pseudocode.min.css">
<script defer src="https://cdn.jsdelivr.net/npm/[email protected]/build/pseudocode.min.js"></script>
<script>
window.addEventListener("load", function () {
if (window.pseudocode) {
pseudocode.renderClass("pseudocode", { lineNumber: true });
}
});
</script>
{% endif %}

{% if page.tikzjax %}
<link rel="stylesheet" href="https://tikzjax.com/v1/fonts.css">
<script src="https://tikzjax.com/v1/tikzjax.js"></script>
{% endif %}
3 changes: 3 additions & 0 deletions _includes/scripts/misc.html
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,6 @@
<script src="{{ '/assets/js/clipboard.js' | relative_url }}"></script>
<script src="{{ '/assets/js/copy_code.js' | relative_url }}"></script>

<!-- Charts / diagrams / pseudocode (opt-in per page via front matter) -->
{% include scripts/diagrams.html %}

44 changes: 44 additions & 0 deletions _includes/search/ninja_keys.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<!-- Full-site command-palette search (ninja-keys). Opens with Cmd/Ctrl-K or the navbar icon. -->
<ninja-keys placeholder="Search pages, posts, projects..." hideBreadcrumbs></ninja-keys>

<script>
(function () {
var items = [
{% assign sorted_pages = site.pages | sort: "nav-title" %}
{% for p in sorted_pages %}{% if p.nav and p.autogen == nil %}
{ title: {{ p.nav-title | default: p.title | jsonify }}, section: "Pages", url: {{ p.url | relative_url | jsonify }}, icon: "fa-file-lines" },
{% endif %}{% endfor %}
{% for post in site.posts %}
{ title: {{ post.title | jsonify }}, section: "Blog", url: {{ post.url | relative_url | jsonify }}, icon: "fa-pen-nib" },
{% endfor %}
{% for pr in site.projects %}
{ title: {{ pr.title | jsonify }}, section: "Projects", url: {{ pr.url | relative_url | jsonify }}, icon: "fa-diagram-project" },
{% endfor %}
];

function mount() {
var ninja = document.querySelector("ninja-keys");
if (!ninja) return;
ninja.data = items.map(function (it) {
return {
id: it.section + ": " + it.title,
title: it.title,
section: it.section,
icon: '<i class="fas ' + it.icon + '"></i>',
handler: function () { window.location.href = it.url; },
};
});
var trigger = document.getElementById("search-toggle");
if (trigger) {
trigger.addEventListener("click", function (e) {
e.preventDefault();
ninja.open();
});
}
}

if (document.readyState !== "loading") mount();
else document.addEventListener("DOMContentLoaded", mount);
})();
</script>
<script type="module" src="https://cdn.jsdelivr.net/npm/[email protected]/+esm"></script>
Loading
Loading