Foodgrain Stock

Food Economy
Author

Pawan

Published

November 1, 2025

Modified

December 25, 2025

Key Points

  • Total foodgrain stocks peaked at 100.5 lakh tonnes (Feb 2025), driven by unmilled paddy (50.1 lakh tonnes).

  • Wheat stocks fluctuated significantly, dropping to 7.5 lakh tonnes (Apr 2024) and rising to 34 lakh tonnes (Feb 2025).

  • Rice stocks steadily increased from 21.1 lakh tonnes (Mar 2023) to 34 lakh tonnes (Feb 2025).

Code
library(data.table)
library(ggplot2)
library(tidyverse)
library(tidyr)
library(scales)
library(ggiraph)
library(lubridate)

## Read and clean data
stocksall <- fread("~/stocks/stocksall.csv", sep = ",", header = TRUE)
stocksall <- unique(stocksall)

oldstocks <- fread("~/stocks/pdsold.csv", sep = ",", header = TRUE)
oldstocks[, coarsegrains := stocks_total - stocks_rice - stocks_wheat]

## Melt stocksall to long format
t <- melt(stocksall, id = c("Year", "Commodity"))

## Convert to Date
t[, Date := as.Date(paste("01", variable, Year, sep = "-"), "%d-%b-%Y")]
t[, Commodity := factor(Commodity, levels = c("Rice", "Wheat", "Unmilled Paddy", "Coarsegrain", "Total"))]

## Process oldstocks
oldstocks <- separate(oldstocks, col = Year, sep = "-", into = c("v1", "year"))
setDT(oldstocks)
oldstocks[, Date := as.Date(paste("01", "04", year, sep = "-"), "%d-%m-%y")]

oldstocks <- oldstocks[Date < as.Date("2003-01-01"),
  .(Date, Rice = stocks_rice, Wheat = stocks_wheat, Coarsegrain = coarsegrains)]

t1 <- melt(oldstocks, id = "Date")
t1[, value := value * 10]  ## Convert to lakh tonnes
setnames(t1, "variable", "Commodity")

## Filter and combine
t_filtered <- t[Commodity != "Total", .(Date, Commodity, value)]
t_filtered <- t_filtered[!is.na(value)]
t1 <- t1[!is.na(value)]

combined <- rbind(t_filtered, t1)
combined[, value := as.numeric(value) / 10]  ## Convert to million tonnes
combined <- combined[!is.na(value)]

## Create stacked data for area plot
stacked_data <- combined[order(Commodity, Date)]
stacked_data[, Commodity := factor(Commodity,
                                   levels = rev(c("Rice", "Wheat", "Unmilled Paddy", "Coarsegrain")))]

## Calculate cumulative sum for stacking
setorder(stacked_data, Date, Commodity)
stacked_data[, cum_value := cumsum(value), by = Date]

## Create tooltip for interactivity
stacked_data[, tooltip := paste0(
  "Date: ", format(Date, "%b %Y"), "\n",
  "Commodity: ", Commodity, "\n",
  "Stock: ", round(value, 1), " million tonnes\n",
  "Percentage: ", round(value/sum(value)*100, 1), "%"
), by = Date]

## Custom theme
my_theme <- theme_bw(base_size = 14) +
  theme(
    text = element_text(family = "sans", color = "#333333"),
    plot.title = element_text(size = 18, face = "bold", hjust = 0.5,
                             margin = margin(10, 0, 10, 0)),
    plot.subtitle = element_text(size = 12, hjust = 0.5, color = "#666666"),
    axis.title = element_text(size = 14),
    axis.text = element_text(size = 12),
    legend.position = "bottom",
    legend.title = element_blank(),
    legend.text = element_text(size = 11),
    panel.grid.major = element_line(color = "#F0F0F0", linewidth = 0.5),
    panel.grid.minor = element_blank(),
    panel.border = element_rect(color = "#CCCCCC", fill = NA, linewidth = 0.8),
    axis.line = element_line(color = "#333333", linewidth = 0.5),
    axis.text.x = element_text(angle = 45, vjust = 1, hjust = 1),
    plot.caption = element_text(size = 10, color = "#666666", hjust = 0)
  )

Create interactive stacked area plot

Code
p <- ggplot(stacked_data,
           aes(x = Date, y = value, fill = Commodity, group = Commodity,
               tooltip = tooltip, data_id = paste(Date, Commodity))) +
  geom_area_interactive(alpha = 0.8, color = "white", linewidth = 0.3) +
  scale_fill_manual(values = c("#4E79A7", "#F28E2B", "#E15759", "#76B7B2")) +
  scale_y_continuous(
    "Stocks (Million Tonnes)",
    labels = scales::comma,
    expand = expansion(mult = c(0, 0.05))
  ) +
  scale_x_date(
    date_breaks = "2 years",
    date_labels = "%Y",
    expand = expansion(mult = c(0.01, 0.01))
  ) +
  labs(
    title = "Foodgrain Stock Holdings (1972-2025)",
    subtitle = "Interactive Area Chart - Hover over areas for detailed breakdown",
    x = "Year",
    caption = "Source: Food Corporation of India | Note: 1 million tonnes = 10 lakh tonnes"
  ) +
  my_theme

## Create zoomed version for recent years
recent_data <- stacked_data[Date >= as.Date("2018-01-01")]

p_recent <- ggplot(recent_data,
                  aes(x = Date, y = value, fill = Commodity, group = Commodity,
                      tooltip = tooltip, data_id = paste(Date, Commodity))) +
  geom_area_interactive(alpha = 0.8, color = "white", linewidth = 0.3) +
  geom_vline_interactive(aes(xintercept = as.Date("2023-04-01"),
                           tooltip = "Start of 2023-24 procurement year"),
                       linetype = "dashed", alpha = 0.5, color = "#333333") +
  scale_fill_manual(values = c("#4E79A7", "#F28E2B", "#E15759", "#76B7B2")) +
  scale_y_continuous(
    "Stocks (Million Tonnes)",
    labels = scales::comma,
    expand = expansion(mult = c(0, 0.05))
  ) +
  scale_x_date(
    date_breaks = "3 months",
    date_labels = "%b %Y",
    expand = expansion(mult = c(0.01, 0.01))
  ) +
  labs(
    title = "Recent Foodgrain Stock Trends (2018-2025)",
    subtitle = "Interactive view with seasonal patterns and procurement cycles",
    x = "Month-Year",
    caption = "Vertical dashed line marks beginning of 2023-24 procurement year"
  ) +
  my_theme +
  theme(axis.text.x = element_text(angle = 45, vjust = 1, hjust = 1, size = 10))

## Make interactive
full_interactive <- girafe(
  ggobj = p,
  width_svg = 14,
  height_svg = 8,
  options = list(
    opts_tooltip(css = "background-color:white;color:black;padding:10px;
                  border-radius:5px;border:1px solid #ddd;font-size:12px;"),
    opts_hover(css = "stroke-width:2;stroke:#333;"),
    opts_selection(type = "single", css = "stroke:#E15759;stroke-width:2;"),
    opts_sizing(rescale = TRUE, width = 1)
  )
)

recent_interactive <- girafe(
  ggobj = p_recent,
  width_svg = 12,
  height_svg = 7,
  options = list(
    opts_tooltip(css = "background-color:white;color:black;padding:10px;
                  border-radius:5px;border:1px solid #ddd;font-size:12px;"),
    opts_hover(css = "stroke-width:2;stroke:#333;"),
    opts_selection(type = "single", css = "stroke:#E15759;stroke-width:2;"),
    opts_sizing(rescale = TRUE, width = 1)
  )
)

## Display both interactive plots
full_interactive
Code
recent_interactive

Create interactive table for last 24 months (using DT package)

Code
library(DT)

last_24_months <- combined[Date >= as.Date("2023-04-01") & Commodity != "Total"]
last_24_wide <- dcast(last_24_months, Date ~ Commodity, value.var = "value", fun.aggregate = sum)
last_24_wide[, Total := rowSums(.SD, na.rm = TRUE), .SDcols = c("Rice", "Wheat", "Unmilled Paddy", "Coarsegrain")]
last_24_wide[, Date := format(Date, "%b-%Y")]

datatable(last_24_wide,
          rownames = FALSE,
          options = list(
            pageLength = 12,
            dom = 'Bfrtip',
            buttons = c('copy', 'csv', 'excel'),
            scrollX = TRUE
          ),
          caption = htmltools::tags$caption(
            style = 'caption-side: top; text-align: center; font-size: 16px; font-weight: bold;',
            'Last 24 Months Foodgrain Stock Position (Million Tonnes)'
          )) %>%
  formatRound(columns = c("Rice", "Wheat", "Unmilled Paddy", "Coarsegrain", "Total"), digits = 1)