Public
Edited
Apr 24, 2023
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
ChoroplethMap(overallStatistics, {
// if no value is passsed, it will look for the first quant property, area in this case
// if no id is passed, it will assume that the features contain the properties
height: 600,
width: 600,
interpolator: d3.interpolate("#FFFFFF", filter1[6]),
strokeWidth: 0.1,
tooltip: (d) => d.properties["zip"],
value: (d) => {
if (
d.properties["routesCount"] >= filter1[1] &&
d.properties["routesCount"] <= filter1[2]
) {
if (d.properties["medianHouseholdIncome"] > filter1[4]) {
return d.properties["averageRent"];
}
}
return 0;
}
})
Insert cell
Insert cell
Insert cell
ChoroplethMap(overallStatistics, {
// if no value is passsed, it will look for the first quant property, area in this case
// if no id is passed, it will assume that the features contain the properties
height: 600,
width: 600,
interpolator: d3.interpolate("#FFFFFF", filter2[6]),
strokeWidth: 0.1,
tooltip: (d) => d.properties["zip"],
value: (d) => {
if (
d.properties["routesCount"] >= filter2[1] &&
d.properties["routesCount"] <= filter2[2]
) {
if (d.properties["medianHouseholdIncome"] > filter2[4]) {
if (
d.properties["averageRent"] >= filter2[8] &&
d.properties["averageRent"] <= filter2[9]
) {
if (
d.properties["averageSellingPrice"] >= filter2[11] &&
d.properties["averageSellingPrice"] <= filter2[12]
) {
return d.properties["costEffectiveness"];
}
}
}
}
return 0;
}
})
Insert cell
Insert cell
Insert cell
ChoroplethMap(overallStatistics, {
// if no value is passsed, it will look for the first quant property, area in this case
// if no id is passed, it will assume that the features contain the properties
height: 600,
width: 600,
interpolator: d3.interpolate("#FFFFFF", filter3[4]),
strokeWidth: 0.1,
tooltip: (d) => d.properties["zip"],
value: (d) => {
switch (filter3[1]) {
case "Luxury (Rent)":
if (d.properties["averageRentRanking"] <= filter3[2]) {
return d.properties["averageRent"];
}
break;
case "Luxury (Selling)":
if (d.properties["averageSellingPriceRanking"] <= filter3[2]) {
return d.properties["averageSellingPrice"];
}
break;
case "Cost-Effectiveness":
if (d.properties["costEffectivenessRanking"] <= filter3[2]) {
return d.properties["costEffectiveness"];
}
break;
default:
console.log("Invalid value");
}
return 0;
}
})
Insert cell
Insert cell
Insert cell
{
// Population living in areas with the condition lower than the given
let populationBelowCondition = 0;
let condition = filter4[2];
for (let feature of overallStatistics.features) {
if (feature.properties.averageRent < condition) {
populationBelowCondition += feature.properties.population;
}
}

// Population living in areas with the condition higher than the given
let populationAboveCondition = 0;
for (let feature of overallStatistics.features) {
if (feature.properties.averageRent >= condition) {
populationAboveCondition += feature.properties.population;
}
}

let totalPopulation = populationBelowCondition + populationAboveCondition;

let populationBelowConditionRatio =
(populationBelowCondition / totalPopulation) * 100;
populationBelowConditionRatio = populationBelowConditionRatio.toFixed(2);
let populationAboveConditionRatio =
(populationAboveCondition / totalPopulation) * 100;
populationAboveConditionRatio = populationAboveConditionRatio.toFixed(2);

let chart = PieChart(
[
{ name: "Greater Than", value: populationAboveConditionRatio },
{ name: "Less Than", value: populationBelowConditionRatio }
],
{
name: (d) => d.name,
value: (d) => d.value,
width,
height: 500
}
);
return chart;
}
Insert cell
### Insight 5: Attributes Details of Target Zip Area
- **Marks:** Area
- Channel: Position
- Color: Ranking percentage of each attribute in all zip areas of the target zip area
- Task: T5
- Arrange: Separate
- Expressiveness: Since we need to display the proportion of multiple attributes at the same time. And they are all percentage data, visualizing them on the same bar chart can let the user know what the advantages and disadvantages are for that area.
- Effectiveness: The position on a chart is the most effective among all channels for ordered attributes.
Insert cell
Insert cell
chart = {
const zipData = overallStatistics.features.find(
(feature) => feature.properties.zip === filter5[1]
).properties;
const indicators = [
{
name: "Economical Ranking",
value: zipData.averageRentReverseRanking / 323
},
{
name: "BART Accessibility Ranking",
value: zipData.routesCountRanking / 323
},
{
name: "Median Household Income Ranking",
value: zipData.medianHouseholdIncomeRanking / 323
},
{
name: "Luxury Ranking (Selling Price)",
value: zipData.costEffectivenessRanking / 323
}
];
let chart = BarChart(indicators, {
x: (d) => d.value,
y: (d) => d.name,
xFormat: "%",
xLabel: "Proportion →",
color: "steelblue",
width,
height: 300,
marginLeft: 200
});

return chart;
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
sellingPriceByZip = {
const res = sellingPriceByZipcodeByMonth;
const filteredData = res
.filter((data) => data.State === "CA")
.map((data) => ({ ...data, Zip: data.RegionName }));

filteredData.forEach((data) => {
let totalPrice = 0;
let count = 0;

for (const key in data) {
if (key.includes("/2022") || key.includes("/2023")) {
const price = parseFloat(data[key]);
if (!isNaN(price)) {
totalPrice += price;
count++;
}
}
}

const averagePrice = count > 0 ? totalPrice / count : 0;
const roundedPrice = Math.round(averagePrice);
data.averagePrice = roundedPrice;
});

return filteredData;
}
Insert cell
rentByZip = {
const res = rentByZipcodeByMonth;
const filteredData = res
.filter((data) => data.State === "CA")
.map((data) => ({ ...data, Zip: data.RegionName }));

filteredData.forEach((data) => {
let totalPrice = 0;
let count = 0;

for (const key in data) {
if (key.includes("/2022") || key.includes("/2023")) {
const price = parseFloat(data[key]);
if (!isNaN(price)) {
totalPrice += price;
count++;
}
}
}

const averagePrice = count > 0 ? totalPrice / count : 0;
const roundedPrice = Math.round(averagePrice);
data.averagePrice = roundedPrice;
});

return filteredData;
}
Insert cell
overallStatistics = {
for (let i = 0; i < bayAreaGeoJSON.features.length; i++) {
const feature = bayAreaGeoJSON.features[i];
const zip = feature.properties.zip;

const incomeInfo = taxableIncomeByZipcode.find((info) => info.Zip === zip);

if (incomeInfo) {
feature.properties.medianHouseholdIncome =
incomeInfo.MedianHouseholdIncome;
feature.properties.population = incomeInfo.Population;
} else {
// if zip not included, set values to 0
feature.properties.medianHouseholdIncome = 0;
feature.properties.population = 0;
}
}

// Merge BART station counts to overall statistics
for (let i = 0; i < bayAreaGeoJSON.features.length; i++) {
const feature = bayAreaGeoJSON.features[i];
const zip = feature.properties.zip;
const accessibilityInfo = bartstationaccessibility.find(
(info) => info.zipCode === zip
);
if (accessibilityInfo) {
feature.properties.routesCount = accessibilityInfo.routesCount;
} else {
feature.properties.routesCount = 0;
}
}

// Merge sellingPriceByZip
for (let i = 0; i < sellingPriceByZip.length; i++) {
const zip = sellingPriceByZip[i].Zip;
const averagePrice = sellingPriceByZip[i].averagePrice;
for (let j = 0; j < bayAreaGeoJSON.features.length; j++) {
const feature = bayAreaGeoJSON.features[j];
if (feature.properties.zip === zip) {
feature.properties.averageSellingPrice = parseInt(averagePrice);
break;
}
}
}

// Merge rentByZip
for (let i = 0; i < rentByZip.length; i++) {
const zip = rentByZip[i].Zip;
const averagePrice = rentByZip[i].averagePrice;
for (let j = 0; j < bayAreaGeoJSON.features.length; j++) {
const feature = bayAreaGeoJSON.features[j];
if (feature.properties.zip === zip) {
feature.properties.averageRent = parseInt(averagePrice);
break;
}
}
}

// Set averageSellingPrice & averageRent 0 if corresponding zip does not exist
for (let i = 0; i < bayAreaGeoJSON.features.length; i++) {
const feature = bayAreaGeoJSON.features[i];
if (!feature.properties.hasOwnProperty("averageSellingPrice")) {
feature.properties.averageSellingPrice = 0;
}
if (!feature.properties.hasOwnProperty("averageRent")) {
feature.properties.averageRent = 0;
}
}

for (let i = 0; i < bayAreaGeoJSON.features.length; i++) {
const feature = bayAreaGeoJSON.features[i];
const { averageRent, averageSellingPrice } = feature.properties;
if (averageRent && averageSellingPrice) {
feature.properties.costEffectiveness = Math.round(
averageSellingPrice / averageRent
);
} else {
feature.properties.costEffectiveness = 0;
}
}

function calculateRankings(data, property) {
// from great to less
const sortedData = data.sort(
(a, b) => b.properties[property] - a.properties[property]
);
sortedData.forEach((zipData, index) => {
const rankingKey = `${property}Ranking`;
zipData.properties[rankingKey] = index + 1;
});
}

function calculateReverseRankings(data, property) {
// from less to great
const sortedData = data.sort(
(a, b) => a.properties[property] - b.properties[property]
);
sortedData.forEach((zipData, index) => {
const rankingKey = `${property}ReverseRanking`;
zipData.properties[rankingKey] = index + 1;
});
}

calculateRankings(bayAreaGeoJSON.features, "population");
calculateRankings(bayAreaGeoJSON.features, "routesCount");
calculateRankings(bayAreaGeoJSON.features, "medianHouseholdIncome");
calculateRankings(bayAreaGeoJSON.features, "averageSellingPrice");
calculateReverseRankings(bayAreaGeoJSON.features, "averageSellingPrice");
calculateRankings(bayAreaGeoJSON.features, "averageRent");
calculateReverseRankings(bayAreaGeoJSON.features, "averageRent");
calculateRankings(bayAreaGeoJSON.features, "costEffectiveness");
calculateReverseRankings(bayAreaGeoJSON.features, "costEffectiveness");

return bayAreaGeoJSON;
}
Insert cell
Insert cell
import { bayAreaGeoJSON } from "@zhenghao-lin/hw7"
Insert cell
import { bartstationaccessibility } from "@zhenghao-lin/hw7"
Insert cell
import { ChoroplethMap } from "bcf45090b3ba1513"
Insert cell
import { PieChart } from "@d3/pie-chart"
Insert cell
import { BarChart } from "@d3/horizontal-bar-chart"
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