Public
Edited
Apr 11
Insert cell
md`# Crop Recommendation Analysis
**By Sai Tarun Vedagiri**
**Student ID: 801421332**

### Dataset: Agricultural Optimal Growth Parameters
This dataset contains NPK (Nitrogen, Phosphorus, Potassium) values,
climate conditions (temperature, humidity, rainfall), and soil pH
requirements for 22 different crops. The data helps farmers
select optimal crops based on their local conditions.
`
Insert cell
md`## Design Decisions & Insights

### Visualization Choices
- **Box plots** for nutrients to show distribution ranges
- **Scatter plot** for climate correlations
- **Combined box+jitter plots** for rainfall variability

### Key Findings
- Rice requires 150+ ppm Nitrogen (3× more than lentils)
- Cotton thrives in hot (>30°C), dry (<60% humidity) conditions
- Chickpeas are most drought-tolerant (50mm minimum rainfall)

### Interaction Rationale
- Linked filtering maintains context across views
- Highlighting enables cross-chart comparison
- Tooltips provide on-demand detail without clutter
`
Insert cell
Insert cell
md`## 🔧 Interactive Controls
Use these filters to explore the data:
`
Insert cell
// CELL 2: Create interactive controls
viewof cropFilter = Inputs.checkbox(
Array.from(new Set(data.map(d => d.label))),
{
label: "Show crops:",
value: Array.from(new Set(data.map(d => d.label))),
sort: true
}
)

Insert cell

viewof highlightedCrop = Inputs.select(
[null, ...new Set(data.map(d => d.label))],
{label: "Highlight crop:"}
)
Insert cell
// ======================
// NUTRIENT VISUALIZATION (FIXED SYNTAX)
// ======================
{
// Check if requirements are met
if (!data || typeof cropFilter === 'undefined' || typeof highlightedCrop === 'undefined') {
return html`<div style="
padding: 1rem;
background: #fff8e1;
border: 1px solid #ffd54f;
border-radius: 4px;
">
<p>⚠️ Please ensure:</p>
<ol>
<li>Data cell has been executed</li>
<li>Control cells have been run</li>
<li>Page was refreshed if needed</li>
</ol>
</div>`;
}

const filteredData = data.filter(d => cropFilter.includes(d.label));
return Plot.plot({
marks: [
// Nitrogen
Plot.boxY(filteredData, {
x: "label",
y: "N",
stroke: "label",
strokeWidth: d => d.label === highlightedCrop ? 3 : 1,
tip: true,
title: d => `${d.label}\nNitrogen: ${d.N} ppm`
}),
// Phosphorus
Plot.boxY(filteredData, {
x: "label",
y: "P",
stroke: "label",
strokeWidth: d => d.label === highlightedCrop ? 3 : 1,
tip: true,
title: d => `${d.label}\nPhosphorus: ${d.P} ppm`
}),
// Potassium
Plot.boxY(filteredData, {
x: "label",
y: "K",
stroke: "label",
strokeWidth: d => d.label === highlightedCrop ? 3 : 1,
tip: true,
title: d => `${d.label}\nPotassium: ${d.K} ppm`
}),
// Highlight dots (only show if a crop is selected)
...(highlightedCrop ? [
Plot.dot(
filteredData.filter(d => d.label === highlightedCrop),
{ x: "label", y: "N", fill: "red", r: 5 }
),
Plot.dot(
filteredData.filter(d => d.label === highlightedCrop),
{ x: "label", y: "P", fill: "red", r: 5 }
),
Plot.dot(
filteredData.filter(d => d.label === highlightedCrop),
{ x: "label", y: "K", fill: "red", r: 5 }
)
] : [])
],
facet: {
data: ["N", "P", "K"],
y: "Nutrient Level",
marginLeft: 60
},
x: {
label: "Crop",
tickRotate: -45
},
y: {
label: "Nutrient Level (ppm)",
grid: true
},
color: {
legend: true,
columns: 3
},
width: 1000,
marginLeft: 70
});
}
Insert cell
// ======================
// ENVIRONMENTAL CONDITIONS
// ======================
{
if (!data || typeof cropFilter === 'undefined' || typeof highlightedCrop === 'undefined') {
return html`<div class="alert">Please run data and control cells first</div>`;
}

const filteredData = data.filter(d => cropFilter.includes(d.label));
return Plot.plot({
marks: [
Plot.dot(filteredData, {
x: "temperature",
y: "humidity",
fill: d => d.label === highlightedCrop ? "red" : "rainfall",
stroke: "label",
strokeWidth: d => d.label === highlightedCrop ? 2 : 1,
r: d => d.label === highlightedCrop ? 7 : 5,
tip: true,
channels: {
Crop: "label",
Temperature: d => `${d.temperature}°C`,
Humidity: d => `${d.humidity}%`,
Rainfall: d => `${d.rainfall} mm`,
pH: "ph"
}
}),
Plot.frame()
],
x: {
label: "Temperature (°C)",
nice: true,
grid: true
},
y: {
label: "Humidity (%)",
nice: true,
grid: true
},
color: [
{
legend: true,
label: "Rainfall (mm)",
scheme: "blues"
},
{
legend: true,
label: "Crop Types",
channel: "stroke",
columns: 2
}
],
width: 850,
height: 550,
marginRight: 120
});
}
Insert cell
// ======================
// RAINFALL DISTRIBUTION
// ======================
{
if (!data || typeof cropFilter === 'undefined' || typeof highlightedCrop === 'undefined') {
return html`<div class="alert">Please run data and control cells first</div>`;
}

const filteredData = data.filter(d => cropFilter.includes(d.label));
const cropStats = d => {
const cropData = filteredData.filter(x => x.label === d.label);
return {
min: Math.min(...cropData.map(x => x.rainfall)),
max: Math.max(...cropData.map(x => x.rainfall)),
median: d3.median(cropData, x => x.rainfall)
};
};

return Plot.plot({
marks: [
Plot.boxY(filteredData, {
x: "label",
y: "rainfall",
stroke: "label",
tip: true,
title: d => `Crop: ${d.label}\nMedian: ${cropStats(d).median.toFixed(1)} mm\nRange: ${cropStats(d).min.toFixed(1)}-${cropStats(d).max.toFixed(1)} mm`
}),
Plot.dot(filteredData, {
x: "label",
y: "rainfall",
fill: "label",
stroke: "black",
strokeWidth: 0.5,
r: 2,
tip: true
}),
highlightedCrop ? Plot.dot(
filteredData.filter(d => d.label === highlightedCrop),
{
x: "label",
y: "rainfall",
fill: "red",
stroke: "black",
r: 6
}
) : null
],
x: {
label: "Crop",
tickRotate: -45
},
y: {
label: "Rainfall (mm)",
grid: true
},
color: {
legend: true,
columns: 4
},
width: 900,
marginBottom: 70
});
}
Insert cell
html`<style>
.alert {
padding: 1rem;
background: #fff8e1;
border: 1px solid #ffd54f;
border-radius: 4px;
margin: 1rem 0;
}
</style>
<div style="color: green; margin-top: 8px;">
Alert styles loaded successfully
</div>`
Insert cell
md`## 🔍 Key Agricultural Insights

### Nutrient Requirements
- **Rice** requires 3-4× more nitrogen than legumes
- **Fruits** show highest potassium demands (200+ ppm)
- **Wheat** has balanced NPK needs (70-100 ppm each)

### Climate Adaptation
- 🌡️ **Cotton** thrives in hot conditions (>30°C)
- 💧 **Rice** requires high humidity (80%+) and rainfall
- ❄️ **Apples** prefer cooler temps (15-20°C)

### Water Management
- 🌧️ **Rice** needs 200-300mm rainfall
- 🌵 **Chickpeas** tolerate drought (50mm minimum)

*Recommendation: Cross-reference nutrient needs with your local soil test results*
`
Insert cell
html`<div style="position: sticky; top: 0; background: white; z-index: 100; padding: 10px 0; border-bottom: 1px solid #eee;">
<a href="#nutrients" style="margin-right: 15px;">Nutrients</a>
<a href="#environment" style="margin-right: 15px;">Environment</a>
<a href="#rainfall" style="margin-right: 15px;">Rainfall</a>
</div>`
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