# ---------------------------------------------------------------------------
# mandi_analysis.py
#
# A year of the price board at the local vegetable mandi, copied down day by
# day, and the questions a household or a small vendor would actually like
# answered:
#
#   1. How does the price of each vegetable move through the year?
#   2. Which vegetable is steady, and which one cannot be planned around?
#   3. When is each vegetable at its cheapest?
#   4. When more of a crop arrives, does the price really fall?
#   5. How bad was the onion spike, and how long did it last?
#
# Prices are per kilogram. Arrivals are in quintals (100 kg).
# ---------------------------------------------------------------------------

import pandas as pd
import matplotlib.pyplot as plt

pd.set_option("display.width", 110)
pd.set_option("display.max_columns", 12)

# --- 1. Read and clean ---------------------------------------------------
df = pd.read_csv("mandi.csv", parse_dates=["date"])
print("Price entries read :", len(df))

before = len(df)
df = df.drop_duplicates()
print("Duplicate entries  :", before - len(df))

# The board is copied by hand, so the same vegetable turns up in two
# spellings. Without this "onion" and "Onion" would be counted separately.
df["vegetable"] = df["vegetable"].str.strip().str.title()

missing = df["price_per_kg"].isnull().sum()
df = df.dropna(subset=["price_per_kg"])
print("Days the board was not noted:", missing, "(dropped)")
print("Entries used       :", len(df))
print("Vegetables         :", sorted(df["vegetable"].unique()))
print("From", df["date"].min().date(), "to", df["date"].max().date())
print()

df["month"] = df["date"].dt.to_period("M").astype(str)

# --- 2. Question 1: the shape of the year --------------------------------
# pivot_table gives one column per vegetable and one row per month, which is
# exactly what a multi-line chart needs.
monthly = df.pivot_table(index="month", columns="vegetable",
                         values="price_per_kg", aggfunc="mean").round(2)

print("--- Average price per kg, month by month ---")
print(monthly)
print()

plt.figure(figsize=(9.5, 5))
for veg in monthly.columns:
    plt.plot(monthly.index, monthly[veg], marker="o", label=veg)
plt.title("Average price per kg at the mandi, month by month")
plt.xlabel("Month")
plt.ylabel("Price per kg (Rs)")
plt.xticks(rotation=45)
plt.legend(fontsize=8)
plt.grid(True, linestyle="--", alpha=0.5)
plt.tight_layout()
plt.savefig("chart1_price_trends.png")
plt.close()

# --- 3. Question 2: steady or wild? --------------------------------------
# The standard deviation says how far a price usually strays from its own
# average. Divided by the average it becomes a percentage, so a Rs 30
# vegetable and a Rs 18 one can be compared fairly.
stats = df.groupby("vegetable")["price_per_kg"].agg(["mean", "std", "min", "max"]).round(2)
stats["swing_percent"] = (stats["std"] / stats["mean"] * 100).round(1)
stats = stats.sort_values("swing_percent", ascending=False)

print("--- How much each price moves ---")
print(stats)
print()
print("Least predictable:", stats.index[0], "at", stats.iloc[0]["swing_percent"], "%")
print("Most predictable :", stats.index[-1], "at", stats.iloc[-1]["swing_percent"], "%")
print()

plt.figure(figsize=(8.5, 4.5))
plt.bar(stats.index, stats["swing_percent"], color="#c9772f")
plt.title("How far each price strays from its own average")
plt.xlabel("Vegetable")
plt.ylabel("Standard deviation as % of the average")
plt.xticks(rotation=20)
plt.tight_layout()
plt.savefig("chart2_volatility.png")
plt.close()

# --- 4. Question 3: when is each one cheapest? ---------------------------
print("--- Cheapest and dearest month for each vegetable ---")
for veg in sorted(monthly.columns):
    col = monthly[veg]
    print("%-12s cheapest %s at Rs %6.2f   dearest %s at Rs %6.2f   (%.1f times)" % (
        veg, col.idxmin(), col.min(), col.idxmax(), col.max(), col.max() / col.min()))
print()

cheapest = monthly.min().sort_values()
dearest = monthly.max()
plt.figure(figsize=(9, 4.5))
x = range(len(cheapest.index))
width = 0.38
plt.bar([i - width / 2 for i in x], cheapest.values, width,
        label="Cheapest month", color="#4c9f70")
plt.bar([i + width / 2 for i in x], dearest[cheapest.index].values, width,
        label="Dearest month", color="#c0653a")
plt.xticks(list(x), cheapest.index, rotation=20)
plt.title("The cheapest and the dearest month for each vegetable")
plt.xlabel("Vegetable")
plt.ylabel("Average price per kg (Rs)")
plt.legend()
plt.tight_layout()
plt.savefig("chart3_cheap_dear.png")
plt.close()

# --- 5. Question 4: do arrivals pull the price down? ---------------------
print("--- Do bigger arrivals mean a lower price? ---")
for veg in sorted(df["vegetable"].unique()):
    part = df[df["vegetable"] == veg]
    # A correlation near -1 means that as one goes up the other goes down.
    r = part["price_per_kg"].corr(part["arrivals_quintal"])
    print("%-12s correlation between price and arrivals: %6.3f" % (veg, round(r, 3)))
print()
print("A negative figure means the price falls when more of the crop arrives,")
print("which is what a market is supposed to do.")
print()

onion = df[df["vegetable"] == "Onion"]
onion_m = onion.groupby("month").agg(price=("price_per_kg", "mean"),
                                     arrivals=("arrivals_quintal", "mean")).round(1)
plt.figure(figsize=(9, 4.5))
plt.plot(onion_m.index, onion_m["arrivals"], marker="s", color="#4c9f70",
         label="Arrivals (quintal per day)")
plt.plot(onion_m.index, onion_m["price"], marker="o", color="#c0392b",
         label="Price (Rs per kg)")
plt.title("Onion: what arrives, and what it costs")
plt.xlabel("Month")
plt.xticks(rotation=45)
plt.legend()
plt.grid(True, linestyle="--", alpha=0.5)
plt.tight_layout()
plt.savefig("chart4_onion_arrivals.png")
plt.close()

# --- 6. Question 5: the onion spike --------------------------------------
print("--- Onion, month by month ---")
print(onion_m)
print()
# The two dearest months are found rather than written into the program. That
# matters for two reasons: the spike moves from year to year, and a program
# that names October in its own code has decided the answer before it looks.
worst = onion_m["price"].nlargest(2).index.tolist()
rest = onion_m["price"].drop(worst)

print("Dearest two months      :", ", ".join(sorted(worst)))
if len(rest) == 0:
    print("There are only two months in the file, so there is nothing to compare them with.")
else:
    normal = rest.mean()
    spike = onion_m.loc[worst, "price"].mean()
    print("Onion in a normal month : Rs", round(normal, 2))
    print("Onion in those two      : Rs", round(spike, 2))
    print("The spike was", round(spike / normal, 2), "times the usual price.")
print("Dearest single day      : Rs", onion["price_per_kg"].max(), "on",
      onion.loc[onion["price_per_kg"].idxmax(), "date"].date())
print()

plt.figure(figsize=(8.5, 4.5))
plt.hist(onion["price_per_kg"].values, bins=16, color="#a05fc0", edgecolor="white")
if len(rest) > 0:
    plt.axvline(rest.mean(), color="#c0392b", linestyle="--", label="Usual price")
plt.title("Onion: how often each price was asked")
plt.xlabel("Price per kg (Rs)")
plt.ylabel("Number of market days")
if len(rest) > 0:
    plt.legend()
plt.tight_layout()
plt.savefig("chart5_onion_spread.png")
plt.close()

monthly.to_csv("monthly_prices.csv")
print("Charts saved : chart1_price_trends.png .. chart5_onion_spread.png")
print("Table saved  : monthly_prices.csv")
