Regression · Decision Support
Crop Recommendation
Which staple crop will perform best under your climate and inputs?
- 0.98
- R² score
- 101
- Countries
- 10
- Staple crops
- 28k+
- Training records
Overview
Choosing what to plant is the single most consequential decision on a farm, yet it is still often made on habit rather than evidence. This tool compares the expected yield (hg/ha) of ten staple crops — maize, cassava, rice, potatoes, sorghum, soybeans, sweet potatoes, wheat, yams and plantains — for a given country, year and set of climate and input conditions.
The model learns from 23 years (1990–2013) of country-level production records combined with average annual rainfall, average temperature and pesticide usage. A farm manager, extension officer or investor can enter local conditions and immediately see which crop the data favours.
The problem
Smallholder and commercial farmers lack a simple, data-driven way to compare how different staple crops are likely to perform under their specific rainfall, temperature and input conditions.
Objectives
Quantify how rainfall, temperature and pesticide use drive yield across ten staple crops
Benchmark linear and non-linear regression models on a large multi-country dataset
Deploy the best model as a free, browser-based decision-support tool
Methodology
-
01
Cleaned and merged FAO yield, rainfall, temperature and pesticide records (28,242 rows, 101 countries)
-
02
Exploratory analysis of yield distributions by crop and country
-
03
Standard-scaled numeric features and one-hot encoded country and crop
-
04
Compared Linear, Lasso, Ridge, K-Nearest Neighbors, Decision Tree and Random Forest regressors
-
05
Selected Random Forest (R² ≈ 0.98) for stability and deployed it with Streamlit
Dataset
FAO / World Bank crop yield dataset — 28,242 records covering 101 countries and 10 staple crops (1990–2013), with yield (hg/ha), average annual rainfall (mm), pesticide use (tonnes) and average temperature (°C).
GentleKing112233/crop-recommendation
Public repository · branch
Explorer
import pandas as pd
import numpy as np
import streamlit as st
import joblib
import os
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import OneHotEncoder, StandardScaler
from sklearn.compose import ColumnTransformer
from sklearn.ensemble import RandomForestRegressor
st.set_page_config(
page_title="Crop Yield Prediction",
page_icon="\U0001F331",
layout="wide"
)
st.markdown("""
<style>
.title {
text-align: center;
font-size: 34px;
font-weight: bold;
color: #111111;
margin-bottom: 15px;
}
.input-panel {
background-color: #858585;
padding: 20px;
border-radius: 5px;
}
.stButton > button {
background-color: #ed4141;
color: white;
width: 100%;
height: 45px;
border: none;
border-radius: 7px;
font-size: 17px;
}
.stButton > button:hover {
background-color: #d93232;
color: white;
}
</style>
""", unsafe_allow_html=True)
CSV_FILE = "yield_df.csv"
MODEL_FILE = "rfr.pkl"
PREPROCESSOR_FILE = "preprocessor.pkl"
COLUMNS = [
'Year', 'average_rain_fall_mm_per_year', 'pesticides_tonnes',
'avg_temp', 'Area', 'Item', 'hg/ha_yield'
]
@st.cache_resource
def load_artifacts():
if os.path.exists(MODEL_FILE) and os.path.exists(PREPROCESSOR_FILE):
model = joblib.load(MODEL_FILE)
preprocessor = joblib.load(PREPROCESSOR_FILE)
return model, preprocessor, True
return None, None, False
@st.cache_data
def load_data(path):
return pd.read_csv(path)
@st.cache_resource
def train_model(df):
data = df[COLUMNS].copy()
X = data.drop(['hg/ha_yield'], axis=1)
y = data['hg/ha_yield']
X_train, _, y_train, _ = train_test_split(
X, y, test_size=0.2, random_state=0, shuffle=True
)
ohe = OneHotEncoder(drop='first')
scaler = StandardScaler()
preprocessor = ColumnTransformer(
transformers=[
('standardscale', scaler, [0, 1, 2, 3]),
('oneHotencode', ohe, [4, 5]),
],
remainder='passthrough'
)
X_train_dummy = preprocessor.fit_transform(X_train)
model = RandomForestRegressor(n_estimators=100, random_state=42)
model.fit(X_train_dummy, y_train)
joblib.dump(model, MODEL_FILE)
joblib.dump(preprocessor, PREPROCESSOR_FILE)
return model, preprocessor
df = load_data(CSV_FILE)
areas = sorted(df["Area"].dropna().unique().tolist())
crops = sorted(df["Item"].dropna().unique().tolist())
model, preprocessor, loaded = load_artifacts()
if not loaded:
model, preprocessor = train_model(df)
loaded = True
st.info("Model trained from the dataset and saved for future runs.")
st.markdown(
'<div class="title">Crop Yield Prediction Per Country</div>',
unsafe_allow_html=True
)
st.markdown('<div class="input-panel">', unsafe_allow_html=True)
st.markdown(
"<h3 style='text-align:center;color:white;'>Input All Features Here</h3>",
unsafe_allow_html=True
)
with st.form("crop_prediction_form"):
col1, col2 = st.columns(2)
with col1:
year = st.number_input("Year", min_value=1900, max_value=2100, value=2013, step=1)
pesticides = st.number_input("Pesticides (tonnes)", min_value=0.0, value=0.0, step=1.0)
area = st.selectbox("Area", areas)
with col2:
rainfall = st.number_input("Average Rainfall (mm/year)", min_value=0.0, value=1000.0, step=10.0)
temperature = st.number_input("Average Temperature (\u00b0C)", value=25.0, step=0.5)
item = st.selectbox("Item", crops)
submitted = st.form_submit_button("Predict", use_container_width=True)
if submitted:
input_data = pd.DataFrame([{
"Year": year,
"average_rain_fall_mm_per_year": rainfall,
"pesticides_tonnes": pesticides,
"avg_temp": temperature,
"Area": area,
"Item": item
}])
try:
transformed_data = preprocessor.transform(input_data)
prediction_hg_ha = model.predict(transformed_data)[0]
prediction_tonnes_ha = prediction_hg_ha / 10000
st.success("Prediction completed successfully!")
st.metric(
label="Predicted Crop Yield",
value=f"{prediction_tonnes_ha:.2f} tonnes per hectare"
)
except Exception as e:
st.error(f"Prediction failed: {e}")
st.markdown("</div>", unsafe_allow_html=True)
Showing a saved copy of the main file — browse the full repository on GitHub.