LambdaLabTM
Informatics Practices · Class 12 Project · Subject Code 065
Sample ProjectData from CSVpandas + matplotlib⏱️ 14 min read

A Year of the Society's Electricity

Twelve months of meter readings for sixty-four flats, turned into slab-wise bills and the four arguments they settle.

1Introduction: the problem it solves

A housing society secretary writes down sixty-four meter readings every month and files the sheet. Once a year the committee meets, the electricity bill is discussed for twenty minutes, and everybody leaves with the opinion they came in with.

There are four real questions underneath that argument. Which months does the money actually go in? Is C block heavier than the others, or simply larger? Which individual flats are drawing far more than the rest? And does a bigger family really use more, or is that just something people say?

All four are answerable from the register the secretary already keeps. This project reads it, applies the slab tariff properly, and answers them in a second.

who would use it

The secretary of a housing society, a resident welfare association, or anybody on a committee who has to explain why the electricity bill went up.

Why it is worth doing on a computer

Two things make this worth computerising. The first is the slab tariff. A domestic bill is not units times a rate — the first hundred units are charged at one rate, the next hundred at a higher one, and so on. Working that out by hand for sixty-four flats, twelve times a year, is 768 multi-step calculations, and the mistake people make is charging the whole bill at the top rate.

The second is that the argument at the annual meeting is never settled, because nobody brings figures to it. A chart showing that the peak month is 1.95 times the lowest ends the conversation about whether summer is really worse. A ranked list of flats ends the conversation about who is heavy. Neither of those can be produced in a meeting, and both take a second on a laptop.

Objectives

  1. To read a year of meter readings for a whole society out of one CSV file
  2. To apply the domestic slab tariff correctly, charging each part of the usage at its own rate
  3. To show how the society's consumption moves through the year and how far the peak is above the trough
  4. To compare blocks fairly, using the average per flat rather than the block total
  5. To rank individual flats, so an unusually heavy meter can be found and investigated
  6. To test whether family size explains consumption, rather than assuming it does
  7. To show the spread of monthly bills, so the committee knows what a typical flat actually pays

2How the job is done today

Before writing anything it is worth asking how the work is handled at present, and where each of those answers falls short. These were examined:

The secretary's reading register

Complete, and where all of this data comes from. It records and does not compute: the slab arithmetic is done separately, usually on a calculator, and the annual questions are never asked of it at all.

The electricity board's own bills

Correct, and useless for this. They arrive one flat at a time, they carry no history, and the society cannot compare its own blocks with them.

A spreadsheet with a slab formula

The nearest alternative. Building the nested IF for four slabs is fiddly, and it is copied down 768 cells where any one of them can be wrong without anybody noticing.

Society management apps

They collect maintenance dues well. Electricity analysis is usually not in them, and where it is, the society's data ends up somewhere it cannot easily get it back from.

3Where the data came from

CBSE asks that any resource used in a project be suitably referenced, and for a data project that rule is not a formality — a figure with no source attached to it does not mean anything. This section is the one an examiner will ask about.

The society's secretary reads every meter in the first week of the month and writes flat number, block, occupants and units into a register. One year of that register — twelve readings for each of sixty-four flats — was copied into readings.csv.

The number of occupants is the only field not on the meter. It comes from the society's own membership list and is the figure the committee already uses for water charges.

The dataset shipped here is a LambdaLab sample of 769 rows standing in for that register, complete with a reading entered twice, five meters that were not read and a block letter typed in lower case. Use your own society's register in your submission, with the secretary's permission, and say in the report which society and which year.

4The dataset

One file in, one file out. readings.csv is the reading register as it is already kept — one row per flat per month. The slab tariff is not in the file: it is in the program, in one place, so a change in the tariff is a one-line edit rather than a hunt.

readings.csv — one row per flat per month

FieldTypeWhat it holds
flat_notextThe flat, e.g. A-101. The block letter is in it, and it is also its own column.
blocktextA, B or C.
occupantsintegerHow many people live in the flat, from the membership list.
monthtext (YYYY-MM)The month the reading is for. Written this way round so it sorts correctly.
unitsintegerUnits consumed that month. Blank if the meter could not be read.

The slab tariff, which lives in the program rather than the file

FieldTypeWhat it holds
First 100 unitsRs 4.50 per unitThe lifeline slab — every flat pays this rate on its first hundred units.
101 to 200Rs 6.00 per unitOnly the units in this band are charged at this rate.
201 to 400Rs 7.75 per unitAnd likewise here.
Above 400Rs 9.20 per unitThe top slab, with no upper limit.
Fixed chargeRs 90.00 per monthAdded to every bill whatever the usage, even a zero one.

The first few lines of readings.csv

flat_noblockoccupantsmonthunits
A-101A32025-07240
A-101A32025-08267
A-101A32025-09215
A-101A32025-10203
A-101A32025-11179
A-101A32025-12144
A-101A32026-01144
A-101A32026-02167

Inside readings.csv

The society's reading register: 769 rows, one per flat per month, with the mistakes a handwritten register has in it. The whole file is 769 rows, 16.5 KB — too much to print here, so this is the head of it. The complete file comes with the download, and you can also take it on its own.

readings.csv
flat_no,block,occupants,month,units
A-101,A,3,2025-07,240
A-101,A,3,2025-08,267
A-101,A,3,2025-09,215
A-101,A,3,2025-10,203
A-101,A,3,2025-11,179
A-101,A,3,2025-12,144
A-101,A,3,2026-01,144
A-101,A,3,2026-02,167

5Cleaning the data

Real data arrives with mistakes in it, and this dataset has the ones real data actually has. What was wrong, how much of it there was, and what the program does about each — because how a problem is handled changes the answer, and a report has to say which choice it made.

A reading entered twice
1 row

drop_duplicates(). A flat has one reading a month, so an exact repeat is a slip of the pen and would otherwise be billed twice.

The meter could not be read
5 rows

dropna(subset=["units"]) drops them and the count is printed, so the secretary knows five meters were missed. Filling them with an average would put a bill on a flat nobody measured.

The block letter typed in lower case
2 rows

str.strip().str.upper(). Left alone, block "a" would appear as a fourth block with two readings in it and a nonsensical average.

units arrives as a decimal
the whole column

A blank anywhere in a column forces pandas to store the whole column as float. astype(int) after the blanks are gone puts it back.

6What the program does

  • Reads a year of meter readings for a whole society from one CSV file
  • Cleans it: removes a repeated reading, drops meters that were not read, and folds a lower-case block letter back in
  • Applies a four-band slab tariff correctly, charging each part of the usage at its own rate
  • Shows the society's consumption month by month and how far the peak is above the trough
  • Compares blocks by total and by average per flat, which are different answers to different questions
  • Ranks the ten heaviest flats of the year
  • Measures how strongly consumption tracks family size, instead of assuming it does
  • Describes the spread of monthly bills across the society
  • Writes a month-by-month summary out as a CSV for the notice board

The pandas and pyplot it is built from

CallWhereWhat it is for
pd.read_csv()step 1Loads the reading register
df.drop_duplicates()step 2Removes a reading entered twice
Series.str.strip().str.upper()step 2Folds block "a" back into block "A"
df.dropna(subset=["units"])step 2Drops the meters that were not read
Series.apply(bill_for)step 3Runs the slab calculation down the whole column
min(units, limit) - usedinside bill_forHow much of the usage falls inside this slab, and no more
df.groupby(col)[v].sum()step 4The month totals and the per-flat totals
df.groupby(col)[v].mean()step 4The block and family-size averages
Series.sort_values(ascending=False)step 4Ranks the flats, heaviest first
Series.corr(other)step 5How closely two columns move together, from -1 to 1
Series.idxmax() / idxmin()step 4Names the peak and the trough month
pd.DataFrame({...})step 4Puts two Series side by side in one table
df.groupby().agg(name=(col, how))step 6Two different sums in one pass, each with the name it should have
Series.describe()step 6The spread of the bills in one call
plt.barh() / plt.hist()steps 4, 6Sideways bars for flat numbers, a histogram for the bills

7Technical details

LanguagePython 3
Where the data livesA plain CSV file, read into pandas
Libraries
  • pandas — reads the register, cleans it, and does every grouping, average and correlation
  • matplotlib.pyplot — draws the five charts and saves each as a PNG

8How it works, step by step

1
Read

read_csv() loads readings.csv — 769 rows covering 64 flats and 12 months.

2
Clean

The repeated reading goes, the block letter is upper-cased, unread meters are dropped and counted, and units goes back to whole numbers.

3
Bill

bill_for() walks the four slabs, charging only the part of the usage inside each. apply() runs it down the whole units column.

4
Group

groupby() gives the monthly totals, the block averages, the per-flat totals and the average by family size.

5
Correlate

corr() puts a number on how closely occupants and units move together, so the committee's assumption can be tested rather than repeated.

6
Draw and save

Five charts — a line, two bars, a horizontal bar and a histogram — each saved with savefig().

9Source code

The whole program. Every chart further down this page was drawn by this listing, and every figure quoted came out of running it.

electricity_analysis.py
# ---------------------------------------------------------------------------
# electricity_analysis.py
#
# A year of meter readings for one housing society, taken from the register
# the secretary keeps, and the five questions the committee argues about at
# every annual meeting:
#
#   1. Which months does the society actually spend its money in?
#   2. Which block uses the most, and is that only because it is bigger?
#   3. Which flats are the heaviest users?
#   4. Does a bigger family really use more, or is that just assumed?
#   5. What does a typical flat pay, once the slab rates are applied?
#
# The tariff is the domestic slab rate; change SLABS to your own state's.
# ---------------------------------------------------------------------------

import pandas as pd
import matplotlib.pyplot as plt

# (up to this many units, rate per unit). The last slab has no upper limit,
# so it is written as a very large number rather than as a special case.
SLABS = [(100, 4.50), (200, 6.00), (400, 7.75), (10 ** 9, 9.20)]
FIXED_CHARGE = 90.00          # a fixed monthly charge, whatever the usage


def bill_for(units):
    """The rupee bill for one month's units, applying each slab in turn."""
    total = FIXED_CHARGE
    used = 0                            # units already charged in a lower slab
    for limit, rate in SLABS:
        if units <= used:               # nothing left to charge
            break
        # Only the part of the usage that falls inside this slab is charged
        # at this slab's rate. This is the point students most often get
        # wrong: the whole bill is NOT charged at the top rate.
        in_slab = min(units, limit) - used
        total = total + in_slab * rate
        used = limit
    return round(total, 2)


# --- 1. Read and clean ---------------------------------------------------
df = pd.read_csv("readings.csv")
print("Readings in the register :", len(df))

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

df["block"] = df["block"].str.strip().str.upper()   # "a" and "A" are one block

# A blank means the meter could not be read that month. It is left out rather
# than filled with a guess, and the count is printed so the secretary knows
# how many meters were missed.
missed = df["units"].isnull().sum()
df = df.dropna(subset=["units"])
df["units"] = df["units"].astype(int)
print("Meters not read          :", missed)
print("Readings used            :", len(df))
print("Flats                    :", df["flat_no"].nunique())
print()

# --- 2. Derive: what each reading costs ----------------------------------
# apply() sends every value of the units column through bill_for().
df["bill"] = df["units"].apply(bill_for)

print("Units for the year   :", df["units"].sum())
print("Billed for the year  : Rs", round(df["bill"].sum(), 2))
print("Average monthly bill : Rs", round(df["bill"].mean(), 2))
print()

# --- 3. Question 1: which months? ----------------------------------------
monthly = df.groupby("month")["units"].sum()

print("--- Units used, month by month ---")
print(monthly)
print()
print("Highest month:", monthly.idxmax(), "at", monthly.max(), "units")
print("Lowest  month:", monthly.idxmin(), "at", monthly.min(), "units")
# The guard is not decoration. A month in which nothing was drawn is unlikely
# in a whole society and perfectly possible in a small one, and without this
# line the program stops with ZeroDivisionError at the last hurdle.
if monthly.min() > 0:
    print("The peak month is", round(monthly.max() / monthly.min(), 2), "times the lowest.")
else:
    print("One month drew nothing at all, so there is no ratio to give.")
print()

plt.figure(figsize=(9, 4.5))
plt.plot(monthly.index, monthly.values, marker="o", color="#e07b39")
plt.title("Units used by the society, month by month")
plt.xlabel("Month")
plt.ylabel("Units (kWh)")
plt.xticks(rotation=45)
plt.grid(True, linestyle="--", alpha=0.5)
plt.tight_layout()
plt.savefig("chart1_monthly_units.png")
plt.close()

# --- 4. Question 2: which block? -----------------------------------------
# Two figures, because they answer different questions. The total says where
# the money goes; the average per flat says whether a block is really heavier
# or simply larger.
block_total = df.groupby("block")["units"].sum()
block_avg = df.groupby("block")["units"].mean().round(1)

print("--- Block by block ---")
print(pd.DataFrame({"total_units": block_total, "average_per_flat_month": block_avg}))
print()

plt.figure(figsize=(8, 4.5))
plt.bar(block_avg.index, block_avg.values, color="#4c9f70")
plt.title("Average monthly units per flat, by block")
plt.xlabel("Block")
plt.ylabel("Average units per flat per month")
plt.tight_layout()
plt.savefig("chart2_block_average.png")
plt.close()

# --- 5. Question 3: which flats are heaviest? ----------------------------
per_flat = df.groupby("flat_no")["units"].sum().sort_values(ascending=False)

print("--- Ten heaviest flats for the year ---")
print(per_flat.head(10))
print()

top10 = per_flat.head(10)[::-1]
plt.figure(figsize=(8, 5))
plt.barh(top10.index, top10.values, color="#3b7dd8")
plt.title("Ten heaviest flats, units for the year")
plt.xlabel("Units (kWh)")
plt.tight_layout()
plt.savefig("chart3_top_flats.png")
plt.close()

# --- 6. Question 4: does family size explain it? -------------------------
by_size = df.groupby("occupants")["units"].mean().round(1)
print("--- Average monthly units by number of occupants ---")
print(by_size)
print()
# corr() gives a number between -1 and 1. Near 1 means the two rise together.
print("Correlation between occupants and units:",
      round(df["occupants"].corr(df["units"]), 3))
print()

plt.figure(figsize=(8, 4.5))
plt.bar(by_size.index.astype(str), by_size.values, color="#c9772f")
plt.title("Average monthly units by size of the family")
plt.xlabel("Number of occupants")
plt.ylabel("Average units per month")
plt.tight_layout()
plt.savefig("chart4_occupants.png")
plt.close()

# --- 7. Question 5: what does a typical bill look like? ------------------
print("--- Monthly bills ---")
print(df["bill"].describe().round(2))
print()

plt.figure(figsize=(8, 4.5))
plt.hist(df["bill"].values, bins=12, color="#a05fc0", edgecolor="white")
plt.title("Spread of monthly bills across the society")
plt.xlabel("Bill for one flat for one month (Rs)")
plt.ylabel("Number of bills")
plt.tight_layout()
plt.savefig("chart5_bill_spread.png")
plt.close()

# --- 8. The sheet the secretary puts on the notice board -----------------
summary = df.groupby("month").agg(units=("units", "sum"),
                                  billed=("bill", "sum")).round(2)
summary.to_csv("month_summary.csv")

print("Charts saved : chart1_monthly_units.png .. chart5_bill_spread.png")
print("Summary saved: month_summary.csv")
⬇️ Take it with you

The full report as a PDF, ready to print and fill in. Or the working project as a zip — the program, the dataset, the charts and a README.

10Sample output

A real run, reproduced exactly as it appeared. Nothing below was typed by hand — it is the transcript of the program above against the dataset in section 4.

Command Prompt
Readings in the register : 769
Duplicate readings removed: 1
Meters not read          : 5
Readings used            : 763
Flats                    : 64

Units for the year   : 152045
Billed for the year  : Rs 910465.8
Average monthly bill : Rs 1193.27

--- Units used, month by month ---
month
2025-07    15168
2025-08    14244
2025-09    13264
2025-10    11721
2025-11     9624
2025-12     8761
2026-01     8910
2026-02     9697
2026-03    11914
2026-04    14888
2026-05    17074
2026-06    16780
Name: units, dtype: int64

Highest month: 2026-05 at 17074 units
Lowest  month: 2025-12 at 8761 units
The peak month is 1.95 times the lowest.

--- Block by block ---
       total_units  average_per_flat_month
block                                     
A            53933                   188.6
B            52811                   184.0
C            45301                   238.4

--- Ten heaviest flats for the year ---
flat_no
C-104    5454
C-107    3599
A-119    3514
B-121    3363
C-108    3352
C-105    3262
A-121    3237
B-105    3169
A-108    3059
B-118    3028
Name: units, dtype: int64

--- Average monthly units by number of occupants ---
occupants
1    122.3
2    180.7
3    208.0
4    231.7
5    324.3
Name: units, dtype: float64

Correlation between occupants and units: 0.566

--- Monthly bills ---
count     763.00
mean     1193.27
std       569.14
min       301.50
25%       798.00
50%      1074.00
75%      1496.50
max      5100.40
Name: bill, dtype: float64

Charts saved : chart1_monthly_units.png .. chart5_bill_spread.png
Summary saved: month_summary.csv

Running it also wrote month_summary.csv12 rows. This is the head of it:

month_summary.csv
month,units,billed
2025-07,15168,92918.2
2025-08,14244,86237.65
2025-09,13264,79054.85
2025-10,11721,68313.05
2025-11,9624,54663.5
2025-12,8761,49343.75
2026-01,8910,50294.25
2026-02,9697,55226.5
2026-03,11914,69509.5

11The charts, and what each one says

CBSE asks for appropriate charts, and the word doing the work in that phrase is appropriate. A line for something that moves in order, a bar to compare things that do not, a histogram for the shape of one column of numbers. Each chart below says which it is, why that kind was chosen, and what it turned out to show.

1The society's year
Line chart
The society's year
how to read it

One point per month, from July to the following June, with the height being every unit the society drew that month. A line chart, because the months are in order and the shape is the message.

what it says

The curve is a clean U turned upside down: 17,074 units in May and 8,761 in December. The peak month is 1.95 times the lowest — the society very nearly doubles its consumption between winter and summer.

There is no way to look at that and go on treating the electricity bill as a fixed monthly cost. The society's money problem is a four-month problem, and any measure that only helps in December is aimed at the wrong end of this chart.

drawn by the code above · saved as chart1_monthly_units.png
2Which block is really the heaviest
Bar chart
Which block is really the heaviest
how to read it

Average units per flat per month, block by block. The average, not the total — C block has 16 flats to A and B's 24 each, so totals would say nothing except which block is bigger.

what it says

C block averages 238.4 units per flat per month against A's 188.6 and B's 184.0 — about 27 per cent more than either.

The totals told the opposite story: A block's 53,933 units for the year is the largest of the three. Both figures are correct and they answer different questions, and this is the trap the committee had fallen into. A block uses the most electricity; C block's flats use the most electricity. Only the second sentence is about behaviour.

drawn by the code above · saved as chart2_block_average.png
3The ten heaviest flats
Horizontal bar chart
The ten heaviest flats
how to read it

Total units for the year for the ten largest meters, biggest at the top, drawn sideways because flat numbers do not fit under vertical bars.

what it says

C-104 drew 5,454 units in the year. The next flat on the list, C-107, drew 3,599 — so the top flat is 52 per cent above the one behind it, which is a much bigger step than any other gap in the list.

That is not an accusation, it is a question worth asking: a gap that size is usually a faulty meter, a second connection or a genuinely different household. It took one chart to find and would never have been noticed in the register.

drawn by the code above · saved as chart3_top_flats.png
4Does a bigger family really use more?
Bar chart
Does a bigger family really use more?
how to read it

Average monthly units for households of one, two, three, four and five people. The bars are in order because the thing on the axis is a count, so the reader is meant to follow it left to right.

what it says

Yes, and steadily: 122.3 units for a single person, rising to 324.3 for a household of five. The correlation between occupants and units is 0.566 — a real relationship, but nothing like a perfect one.

That 0.566 is the most useful number in the project. It says family size explains part of the difference between flats and nowhere near all of it, so the society cannot bill by household size and call it fair, and neither can it dismiss a heavy flat as "they are a big family" without looking.

drawn by the code above · saved as chart4_occupants.png
5What a flat actually pays
Histogram
What a flat actually pays
how to read it

All 763 monthly bills sorted into twelve bands, with the height being how many bills fell into each. A histogram, because the question is the shape of the spread rather than any single flat.

what it says

The median bill is Rs 1,074 and the mean Rs 1,193.27 — the mean is higher because of the tail on the right, which runs out to a single bill of Rs 5,100.40.

The smallest bill of the year was Rs 301.50. The society therefore contains flats whose bills differ by a factor of seventeen in the same month, under the same tariff, which is worth knowing before anybody proposes charging electricity as a flat rate per flat.

drawn by the code above · saved as chart5_bill_spread.png

12What the analysis found

the findings, in one line each
  • The society used 152,045 units in the year and was billed Rs 910,465.80.
  • May is the peak month at 17,074 units and December the lowest at 8,761 — a ratio of 1.95.
  • C block averages 238.4 units per flat per month against A's 188.6 and B's 184.0.
  • A block has the highest total consumption and the second-lowest per flat; total and average disagree.
  • One flat, C-104, drew 5,454 units — 52 per cent more than the next heaviest.
  • Consumption rises with family size, from 122.3 units for one person to 324.3 for five, at a correlation of 0.566.
  • A typical monthly bill is Rs 1,074, but they run from Rs 301.50 to Rs 5,100.40.

What should be done about them

This is the part that turns an analysis into a project. A chart that nobody acts on is a picture; a recommendation somebody can argue with is a result.

  1. Plan the society's cash for a four-month summer peak rather than a flat monthly bill.
  2. Have C-104's meter checked. A gap of that size usually has a physical explanation.
  3. Stop comparing blocks by total. Put the per-flat average on the notice board instead.
  4. Do not bill by household size. At a correlation of 0.566 it would be unfair to about half the society.
  5. Read every meter. Five missed readings in a year is five bills that had to be estimated.
  6. Put the month-wise chart on the notice board in March, before the summer, not in July when the bill has already arrived.

13Testing

Every case below was actually executed and its result recorded as it appeared — including the ones expected to fail. Each one builds a small dataset of its own and runs the whole program against it.

Test caseExpectedActualResult
The full register, 769 readingsUnits for the year : 152045Units for the year : 152045Pass
Meters that were not read are counted and droppedMeters not read : 5Meters not read : 5Pass
A reading entered twice is removedDuplicate readings removed: 1Duplicate readings removed: 1Pass
Block 'a' is folded back into block 'A'Flats : 64Flats : 64Pass
A flat using exactly 0 unitsAverage monthly bill : Rs 90.0Average monthly bill : Rs 90.0Pass
A flat using exactly 100 unitsAverage monthly bill : Rs 540.0Average monthly bill : Rs 540.0Pass
A flat using exactly 101 unitsAverage monthly bill : Rs 546.0Average monthly bill : Rs 546.0Pass
A flat using exactly 200 unitsAverage monthly bill : Rs 1140.0Average monthly bill : Rs 1140.0Pass
A flat using exactly 201 unitsAverage monthly bill : Rs 1147.75Average monthly bill : Rs 1147.75Pass
A flat using exactly 400 unitsAverage monthly bill : Rs 2690.0Average monthly bill : Rs 2690.0Pass
A flat using exactly 401 unitsAverage monthly bill : Rs 2699.2Average monthly bill : Rs 2699.2Pass
A flat that used nothing still pays the fixed chargeBilled for the year : Rs 90.0Billed for the year : Rs 90.0Pass
An unread meter is not given an estimated billReadings used : 1Readings used : 1Pass
...and the year's units are the one real readingUnits for the year : 240Units for the year : 240Pass
Block totals and block averages disagree, correctlyA 200 100.0A 200 100.0Pass
...the smaller block has the higher averageC 150 150.0C 150 150.0Pass
readings.csv missing altogetherFileNotFoundError: [Errno 2] No such file or directory: 'readings.csv'FileNotFoundError: [Errno 2] No such file or directory: 'readings.csv'Pass

Two kinds of case are in there on purpose. The boundary cases test the edge of a rule, where a program is most often wrong by one. The failure cases check that it stops cleanly and says why, instead of quietly producing a wrong answer.

14Advantages

Set against the ways the job is done today:

  • The slab tariff is applied the same way for every flat every month, which hand calculation cannot promise
  • The tariff sits in one place, so a change in the rates is a one-line edit
  • Blocks are compared fairly, by the average per flat rather than by size
  • An unusual meter is found in a second instead of never
  • An assumption the committee had been repeating for years is tested and given a number
  • The whole year is re-analysed with one command when next year's readings are added

15Limitations and future scope

What this version cannot tell you

A data project should be honest about the limits of its own data. Each of these is a reason for one of the additions below:

  • It bills domestic slabs only — there is no separate commercial or common-area tariff
  • Common lighting, lifts and pumps are not in the register at all, so this is flats only
  • The number of occupants is taken as fixed for the year, and families change
  • A missed reading is dropped rather than estimated, so five months of five flats are simply absent
  • It says nothing about why a flat is heavy — a geyser, an air conditioner and a faulty meter look identical here

What to add next

This is also where you make the project yours. Take one or two of these, or something nobody here thought of:

  • Add the common-area meter and show what the society pays before any flat is billed
  • Print a one-page bill per flat, ready to be slipped under the door
  • Compare this year with last year on one chart
  • Flag any flat whose consumption jumps more than 50 per cent over its own average, which is how a fault shows up
  • Add a solar generation column and show what it actually saved
  • Let the tariff be read from a small file, so the treasurer can update it without opening the program

16What you may have to teach yourself

CBSE expects some self-learning in a project and says so. For this one, that means:

  • How a slab tariff really works. Look at a real electricity bill and find the bands — the common mistake of charging everything at the top rate is worth making once, on paper, to see how wrong it goes.
  • corr(), what a correlation between -1 and 1 means, and why 0.566 is neither nothing nor proof
  • The difference between a total and an average, which is the whole of this project's second chart
  • Your own state's domestic tariff, from the electricity board's website — the slabs here are a plausible set, not your state's

17Conclusion

The program does what it set out to do. A year of meter readings, kept exactly as the secretary already keeps them, comes back as correct slab-wise bills and five charts, and the four questions the annual meeting argues about now have figures attached.

Two of the findings changed what the committee believed. A block uses more electricity than C block and C block's flats use more than A block's — both true, and the society had been quoting the first while meaning the second. And the assumption that a big family explains a big bill turned out to be half right: a correlation of 0.566 is real, and it is not enough to bill anybody on.

The part that needed the most care was the slab function. It is eleven lines and it is the only place in the project where a wrong answer would look completely reasonable — a bill charged entirely at the top rate is still a plausible-looking number. That is why it is the part with a test at every band edge, from below and from above.

Writing those tests found a real fault, which is the argument for writing them. A society in which some month drew nothing at all made the program stop with ZeroDivisionError on the line that compares the peak month with the lowest. It would never have happened on the full register and it happens immediately on a single flat, which is exactly the kind of case a person testing by eye does not think to try. The line is now guarded, and the guard is in the listing.

18References

Every report needs a bibliography, and a data project needs its data source at the top of it.

  • The reading register of one housing society, one year, with the secretary's permission. The dataset shipped here is a LambdaLab sample standing in for it.
  • The domestic slab tariff of a state electricity regulatory commission — the bands in this program are a representative set, and yours should be your own state's published tariff, cited by date.
  • pandas user guide, “Group by: split-apply-combine” — https://pandas.pydata.org/docs/user_guide/groupby.html
  • Informatics Practices, Class XII — the NCERT / CBSE prescribed textbook, for the chapters on data handling with pandas and data visualisation
  • pandas documentation — https://pandas.pydata.org/docs/
  • Matplotlib documentation — https://matplotlib.org/stable/
  • CBSE Senior School Curriculum, Informatics Practices (Subject Code 065) — the project guidelines this report follows
  • LambdaLab — https://www.lambdalab.in
Key Takeaway
The PDF is the whole report. Cover page, certificate, acknowledgement, index, everything on this page and the bibliography — in the order CBSE marks them, ready to print. The cover page, certificate and acknowledgement arrive with blank rules where the names go, because a certificate with somebody else's name printed on it is not a template. Fill those in, get the certificate signed, and replace the data with data you collected yourself.