| name | csv-data-handling |
| description | Loading and parsing CSV files with D3.js, data transformation, and handling missing values |
CSV Data Handling with D3.js
Overview
D3.js provides built-in CSV parsing. Understanding type coercion and data transformation is essential for visualization.
1. Loading CSV Files
Basic CSV Load
d3.csv("data.csv").then(data => {
console.log(data);
});
With Error Handling
d3.csv("data.csv")
.then(data => {
console.log("Data loaded:", data.length, "rows");
processData(data);
})
.catch(error => {
console.error("Error loading CSV:", error);
});
With Type Accessor
D3 can automatically convert types:
d3.csv("data.csv", row => {
return {
ticker: row.ticker,
marketCap: +row.marketCap,
sector: row.sector,
value: parseFloat(row.value)
};
}).then(processData);
2. Data Transformation
Parsing Numeric Values
data.forEach(d => {
d.marketCap = +d.marketCap;
d.employees = parseInt(d.employees, 10);
d.yield = parseFloat(d.yield);
});
Handling Missing Values
data = data.filter(d => {
return d.marketCap && d.sector;
});
data.forEach(d => {
d.marketCap = d.marketCap || 0;
d.website = d.website || "N/A";
});
Filtering and Sorting
const tech = data.filter(d => d.sector === "Information Technology");
data.sort((a, b) => b.marketCap - a.marketCap);
const top50 = data.sort((a, b) => b.marketCap - a.marketCap).slice(0, 50);
3. Loading Multiple Files
Sequential Loading
Promise.all([
d3.csv("companies.csv"),
d3.csv("prices.csv")
]).then(([companies, prices]) => {
const merged = mergeData(companies, prices);
visualize(merged);
});
Loading Individual Stock Data
d3.csv("data/stock-descriptions.csv").then(stocks => {
const pricePromises = stocks.map(stock =>
d3.csv(`data/indiv-stock/${stock.ticker}.csv`)
.then(prices => ({
ticker: stock.ticker,
prices: prices
}))
);
return Promise.all(pricePromises);
}).then(allData => {
visualize(allData);
});
4. Aggregation & Grouping
Group by Category
const bySetor = d3.group(data, d => d.sector);
const sectorGroups = Array.from(bySetor, ([sector, stocks]) => ({
sector,
count: stocks.length,
totalCap: d3.sum(stocks, d => d.marketCap)
}));
Nesting (Hierarchical Grouping)
const nested = d3.nest()
.key(d => d.sector)
.entries(data);
5. Formatting Numbers for Display
Format for Visualization
const capFormatter = d3.format(".2s");
const capFormat = (value) => {
const sizes = ['', 'K', 'M', 'B', 'T'];
let sizeIndex = 0;
let num = value;
while (num >= 1000 && sizeIndex < sizes.length - 1) {
num /= 1000;
sizeIndex++;
}
return num.toFixed(2) + sizes[sizeIndex];
};
console.log(capFormat(1641026945024));
Format Percentages
const percentFormatter = d3.format(".2%");
console.log(percentFormatter(0.28806));
6. Data Validation
Check for Required Fields
function validateData(data) {
return data.every(d => {
return d.ticker &&
d.sector &&
d.marketCap !== undefined;
});
}
if (!validateData(data)) {
console.error("Invalid data structure");
}
7. Copying Data for Performance
Deep Copy to Avoid Mutations
const copy1 = [...data];
const copy2 = data.slice();
const copy3 = JSON.parse(JSON.stringify(data));
const lightweightData = data.map(d => ({
ticker: d.ticker,
sector: d.sector,
marketCap: d.marketCap
}));
Common Patterns
Parse and Enrich
d3.csv("stocks.csv")
.then(stocks => {
stocks.forEach(d => {
d.marketCap = +d.marketCap;
d.employees = +d.employees;
});
stocks = stocks.filter(d => d.marketCap > 0);
stocks.forEach(d => {
d.capFormatted = capFormatter(d.marketCap);
d.category = categorizeByMarketCap(d.marketCap);
});
return stocks;
})
.then(processedData => visualize(processedData));
Tips & Gotchas
- CSV string "1000000" stays a string until converted
- Empty cells become empty strings "", not null
- Use
d => d.value to access object properties, not d["value"]
- D3's CSV parser handles quoted fields with commas automatically
- Use
Promise.all() for loading multiple files in parallel
- Always validate data structure before visualizing