Home / Computer Science / Class XII / Data Handling Using Pandas - I
Data Handling Using Pandas - I — CUET Computer Science hero
Class XII 💻 Computer Science Ch 15 of 20

Data Handling Using Pandas - I

CUET unit: Data Handling Using Pandas - I

📌 Snapshot

  • Pandas Series and DataFrame made clear: creation, indexing, slicing, arithmetic and CSV files with examples.
  • Why Pandas exists when NumPy is there, and how a Series differs from an ndarray.
  • Label versus position, and the slicing rule that flips between them: end excluded or included.
  • Missing values (NaN) in arithmetic, fill_value, adding and dropping rows and columns, and reading CSV data.

📖 Detailed Notes

Written and reviewed by Rohit Singh, Computer Science & Commerce faculty at UniDrill (MCA, Osmania University · 6 years teaching). Based on NCERT Informatics Practices Class XII, Chapter 2.

A spreadsheet that lives inside Python

NumPy arrays are fast, but a NumPy array has no column names and holds one data type. A class result sheet has student names, subjects, marks and remarks, with text and numbers mixed and labels you want to use. Pandas gives you exactly that: labelled, mixed type, spreadsheet like data that you can load from a CSV file, filter, edit and save, all in a few lines. The name comes from "PANel DATa".

Pandas is built on NumPy, and it works hand in hand with Matplotlib for charts. You install it with pip install pandas (Python must already be installed) and import it with import pandas as pd. The alias pd is only a convention.

Pandas versus NumPy

Pandas NumPy
A DataFrame can hold different data types (int, float, string, date) An array holds one data type
Simple tools for loading files, selecting, joining and grouping Focused on numeric array computation
Rows and columns have labels, so data is easy to track Elements are reached by integer position
Best for tabular data Best for numeric array data

Pandas has two main data structures in this book: the Series (one dimensional) and the DataFrame (two dimensional).

Series: a labelled column

A Series is a one dimensional sequence of values of any type, each with a label called its index. Think of a single spreadsheet column. If you give no index, the labels are the integers 0 to N minus 1.

Created from Example Index you get
A list pd.Series([10, 20, 30]) 0, 1, 2
A list with your own index pd.Series(["Kavi", "Shyam", "Ravi"], index=[3, 5, 1]) 3, 5, 1
A string index pd.Series([2, 3, 4], index=["Feb", "Mar", "Apr"]) Feb, Mar, Apr
A 1-D NumPy array pd.Series(np.array([1, 2, 3, 4])) 0 to 3
A dictionary pd.Series({'India': 'NewDelhi', 'UK': 'London'}) The dictionary keys

If you pass an index along with an array, its length must equal the number of values. Four values with three labels raise a ValueError (length of values does not match the index).

Code
import pandas as pd
cap = pd.Series(['NewDelhi', 'WashingtonDC', 'London', 'Paris'],
                index=['India', 'USA', 'UK', 'France'])
print(cap)
Output
India       NewDelhi
USA     WashingtonDC
UK            London
France         Paris
dtype: object

The right hand column shows the values and the left hand column shows the index. Text values give dtype: object.

Reading a Series: position or label

There are two kinds of index. The positional index is the integer position starting at 0. The labelled index is whatever label you assigned. Both work for reading, and a list of them selects several elements.

Expression Result
cap['India'] NewDelhi (by label)
cap[1] WashingtonDC (by position)
cap[['UK', 'USA']] UK London and USA WashingtonDC, in that order
cap[[3, 2]] France Paris, then UK London
cap[1:3] USA and UK (position slice, end excluded)
cap['USA':'France'] USA, UK and France (label slice, end included)
cap[::-1] The Series in reverse order

This is the single most tested rule of the chapter: position slices exclude the end, label slices include it. A way to remember: positions are counted by the machine, which stops before the end; labels are names that you chose on purpose, so the named last one belongs to the answer.

Worked example: slicing and updating. A Series sa has values 10 to 15 and index a to f.

Code
sa = pd.Series([10, 11, 12, 13, 14, 15], index=list('abcdef'))
sa[1:3] = 50
sa['c':'e'] = 500
print(sa)
Output
a     10
b     50
c    500
d    500
e    500
f     15
dtype: int64
Step Thinking Effect
1 sa[1:3] is a position slice, so positions 1 and 2, with 3 excluded b and c become 50
2 sa['c':'e'] is a label slice, so c, d and e c, d and e become 500
3 The final value of c is 500, since the second statement overwrote the first a and f never changed

The wrong step is treating the label slice like a position slice and stopping at d.

Attributes and methods of a Series

Item What it gives Example
name Assigns a name to the Series cap.name = 'Capitals'
index.name Assigns a name to the index cap.index.name = 'Countries'
values The data as a NumPy array cap.values
size Number of values, including NaN 4 for cap
empty True if the Series has no elements pd.Series().empty gives True
head(n) First n values; default 5 s.head(2)
tail(n) Last n values; default 5 s.tail(2)
count() Number of values that are not NaN For pd.Series([12, np.nan, 10]) it gives 2

Pause and predict: for s = pd.Series([12, np.nan, 10]), what are s.size and s.count()?

Check your answer: size is 3 because the missing value still occupies a slot. count() is 2 because it counts only real values. That gap between the two is the number of missing values.

Arithmetic on Series: matching by label

Operations between two Series pair elements by index label, not by position. This is index matching (alignment). Labels present in only one Series produce NaN (Not a Number), which marks a missing value, so the operation does not fail.

Worked example: addition and subtraction. Let seriesA = pd.Series([1,2,3,4,5], index=['a','b','c','d','e']) and seriesB = pd.Series([10,20,-10,-50,100], index=['z','y','a','c','e']).

Label In seriesA In seriesB seriesA + seriesB add with fill_value=0 sub with fill_value=1000
a 1 -10 -9.0 -9.0 11.0
b 2 missing NaN 2.0 -998.0
c 3 -50 -47.0 -47.0 53.0
d 4 missing NaN 4.0 -996.0
e 5 100 105.0 105.0 -95.0
y missing 20 NaN 20.0 980.0
z missing 10 NaN 10.0 990.0
Step Thinking
1 The result has the union of both indexes: a, b, c, d, e, y, z (7 labels, sorted)
2 Labels in both (a, c, e) are computed normally
3 Labels in one only (b, d, y, z) give NaN with the operator
4 The methods add, sub, mul and div accept fill_value, which replaces a missing side before calculating. With seriesA.add(seriesB, fill_value=0), seriesB has no value at label b, so it is treated as 0 and we get 2 + 0 = 2
5 The result is float, since NaN is a float value

Division shows why the fill value matters: seriesA.div(seriesB, fill_value=0) gives inf at labels b and d (2 divided by 0 and 4 divided by 0) and 0.0 at y and z (0 divided by 20 and by 10). The operators + - * / are equivalent to add sub mul div without a fill value. Note that fill_value is applied only where one side is missing, not where both are.

Beyond the textbook. For the exam: a Series can be read as cap[1] by position or cap['USA'] by label. In reality: newer versions of Pandas warn when an integer is used on a Series whose index is not made of integers, because it is ambiguous whether you mean the label or the position, and they are moving towards treating such integers as labels only. The clear forms are cap.iloc[1] for position and cap.loc['USA'] for label. Why it matters: when your index itself is made of integers, as in pd.Series([...], index=[3, 5, 1]), s[1] means the label 1, not the second element.

Series versus NumPy ndarray

Series NumPy ndarray
Has labelled indexes that you can define (numbers or letters) Accessed only by integer position
Operations align on labels; unmatched labels give NaN Operations need matching shapes; there is no NaN based alignment
Can be reversed or indexed by labels in any order Index starts at 0 and is fixed
Needs more memory Needs less memory

DataFrame: a table with row and column labels

A DataFrame is a two dimensional labelled structure like a table in MySQL or a spreadsheet. It has a row index and a column index, and each column can hold a different data type. Each column of a DataFrame is a Series, and a DataFrame can be thought of as a dictionary of Series.

Created from How it behaves
Nothing pd.DataFrame() gives an empty DataFrame
A NumPy array or arrays Each array becomes a row; shorter ones are padded with NaN
A list of dictionaries One row per dictionary; keys become columns; missing keys give NaN
A dictionary of lists Keys are column labels and each list is a column
A list of Series Each Series is a row; its labels become columns (union of labels)
A dictionary of Series Keys are column labels; the row labels are the union of all the Series indexes

Worked example: dictionary of lists.

Code
dictForest = {'State': ['Assam', 'Delhi', 'Kerala'],
              'GArea': [78438, 1483, 38852],
              'VDF': [2797, 6.72, 1663]}
dFrameForest = pd.DataFrame(dictForest)
print(dFrameForest)
Output
    State  GArea      VDF
0   Assam  78438  2797.00
1   Delhi   1483     6.72
2  Kerala  38852  1663.00
Thinking Result
The dictionary has 3 keys, so 3 columns Columns State, GArea, VDF
Each list has 3 values, so 3 rows Row labels 0, 1, 2 by default
VDF mixes ints and a float, so the column is promoted to float 2797.00, 6.72, 1663.00
Different columns may have different types State holds text, GArea integers

Passing columns=['State', 'VDF', 'GArea'] reorders the columns. For a list of dictionaries, [{'a': 10, 'b': 20}, {'a': 5, 'b': 10, 'c': 20}] gives 2 rows and 3 columns (a, b, c), with NaN in column c of the first row. The number of columns is the maximum number of keys in any dictionary.

Now take a dictionary of Series holding marks, with the subjects as the row index:

Code
ResultSheet = {
    'Arnab': pd.Series([90, 91, 97], index=['Maths', 'Science', 'Hindi']),
    'Ramit': pd.Series([92, 81, 96], index=['Maths', 'Science', 'Hindi']),
    'Samridhi': pd.Series([89, 91, 88], index=['Maths', 'Science', 'Hindi']),
    'Riya': pd.Series([81, 71, 67], index=['Maths', 'Science', 'Hindi']),
    'Mallika': pd.Series([94, 95, 99], index=['Maths', 'Science', 'Hindi'])}
ResultDF = pd.DataFrame(ResultSheet)
Arnab Ramit Samridhi Riya Mallika
Maths 90 92 89 81 94
Science 91 81 91 71 95
Hindi 97 96 88 67 99

The students are the columns and the subjects are the rows, because the dictionary keys became the column labels.

Attributes of a DataFrame

Attribute What it gives
index Row labels
columns Column labels
dtypes Data type of each column
values A NumPy array of the data without labels
shape Tuple (rows, columns)
size Total number of values (rows times columns)
T The transpose: rows and columns exchange places
head(n), tail(n) First or last n rows (default 5)
empty True if there is no data

For ResultDF the shape is (3, 5) and the size is 15. For the forest data in the next table, shape is (4, 3) and size is 12.

Rows and columns: add, change, delete, rename

Task Code Notes
Add a column ResultDF['Preeti'] = [89, 78, 76] A new label creates a new column at the end. An existing label updates the column
Set a whole column to one value ResultDF['Arnab'] = 90 Every row of that column becomes 90
Add a row ResultDF.loc['English'] = [85, 86, 83, 80, 90, 89] A new label creates a row. An existing label updates that row
Delete a row ResultDF.drop('Science', axis=0) axis=0 means rows
Delete columns ResultDF.drop(['Ramit', 'Riya'], axis=1) axis=1 means columns
Rename rows ResultDF.rename({'Maths': 'Sub1'}, axis='index') Labels you do not mention stay unchanged
Rename columns ResultDF.rename({'Arnab': 'Student1'}, axis='columns') Same rule

Wrong lengths are errors: a new row with fewer values than columns raises ValueError (mismatched columns), and a new column with fewer values than rows raises ValueError (length does not match the index). Also, drop and rename return a new DataFrame, so you assign the result back (ResultDF = ResultDF.drop('Science', axis=0)), otherwise nothing seems to change. If several rows share the same label, drop removes all of them.

Worked example: update or add? ResultDF has rows Maths, Science and Hindi. What happens with ResultDF.loc['English'] = [85, 86, 83, 80, 90, 89] (after the Preeti column was added), and then the same line with different marks?

Step Thinking Result
1 English is not an existing row label A new 4th row is added
2 Run again with new numbers English now exists, so that row is updated, not duplicated
3 ResultDF.loc['Maths'] = 0 Every value in the Maths row becomes 0

Accessing data with loc

DataFrame.loc[] selects by labels. A single row label returns that row as a Series. A single column label (after a colon) returns the column as a Series. A list of labels returns a DataFrame.

Expression Returns
ResultDF.loc['Science'] Row Science as a Series
ResultDF.loc[:, 'Arnab'] Column Arnab as a Series (same as ResultDF['Arnab'])
ResultDF.loc[['Science', 'Hindi']] A DataFrame of those two rows
ResultDF.loc['Maths':'Science'] Rows Maths and Science (label slice, end included)
ResultDF.loc['Maths':'Science', 'Arnab':'Samridhi'] 2 rows by 3 columns
ResultDF.loc['Maths':'Science', ['Arnab', 'Samridhi']] 2 rows by 2 chosen columns
ResultDF.loc[[True, False, True]] Rows Maths and Hindi (Boolean list: True keeps the row)
ResultDF.loc['Maths'] > 90 True or False for each student's Maths mark

Boolean indexing works on the actual values. ResultDF.loc['Maths'] > 90 returns False for Arnab (90), True for Ramit (92), False for Samridhi (89), False for Riya (81) and True for Mallika (94). Notice the rule again: slicing in a DataFrame with loc includes the end label. If you ask for a label that does not exist, you get a KeyError.

Pause and predict: for the ResultDF above, what does ResultDF.loc['Maths':'Hindi', 'Riya'] return?

Check your answer: The Riya column for all three rows from Maths to Hindi (the end label is included), as a Series: 81, 71 and 67. Because the column is a single label, you get a Series and not a DataFrame.

Pause and predict: for pd.DataFrame([10, 20, 30, 40, 50]), what does .loc[2] return, the third row or the row labelled 2?

Check your answer: The row labelled 2, which happens to be the third row here: the value 30. In loc, an integer is always a label, never a position.

Joining DataFrames with append

The chapter uses append() to attach the rows of one DataFrame below another. Columns that are missing in the first one are added, and gaps become NaN.

Code
dFrame1 = dFrame1.append(dFrame2)

With dFrame1 having columns C1, C2, C3 and dFrame2 having C2, C5, the result has four columns C1, C2, C3, C5 and all the rows of both, with NaN wherever a table had no value. Two parameters appear in questions:

Parameter Effect
ignore_index=True Row labels are discarded and renumbered 0, 1, 2 and so on
verify_integrity=True Raises an error if the row labels repeat (default False, so duplicate labels such as R2 are allowed)
sort=True Sorts the column labels

Beyond the textbook. For the exam: DataFrame.append() merges two DataFrames. In reality: append() was removed in Pandas 2.0, and current code uses pd.concat([dFrame1, dFrame2]), with the same ignore_index option. The result is identical. Why it matters: if code copied from a book fails with "DataFrame has no attribute append", this is the reason.

Importing and exporting CSV files

A CSV (comma separated values) file is a plain text file where each line is a record and values are separated by commas. Spreadsheet programs open it directly.

Task Code Key parameters
CSV to DataFrame marks = pd.read_csv("ResultData.csv", sep=",", header=0) sep is the separator (default is a comma). header=0 uses the first line as column names. names=[...] gives your own column names
DataFrame to CSV ResultDF.to_csv("resultout.csv", sep=",") header=False leaves out the column names. index=False leaves out the row labels

Worked example: import then export. The file ResultData.csv has the header RollNo,Name,Eco,Maths and three lines of data.

Code
marks = pd.read_csv("ResultData.csv", header=0)
print(marks.shape)
marks.to_csv("out.txt", sep='@', header=False, index=False)
Step Thinking Result
1 header=0 makes the first line the column names Columns RollNo, Name, Eco, Maths
2 Three data lines become three rows shape is (3, 4)
3 In the export, sep='@' replaces the commas Lines look like 1@Arnab@18@57
4 header=False and index=False No column names and no row numbers in the file

The wrong step is forgetting index=False: the output then begins every line with the row label 0, 1, 2.

Beyond the textbook. For the exam: read_csv takes the file path, sep and header. In reality: when the first line is not a header, pass header=None and supply names, otherwise the first data line is eaten as column titles. Why it matters: a CSV with an unexpected header shows up as wrong column names and one fewer row than you expected.

Exam traps

Trap Why it is tempting The truth
"s[1:3] and s['b':'d'] behave the same" Both are slices Position slice excludes the end; label slice includes it
"Adding two Series adds by position" NumPy does Pandas matches by label; unmatched labels give NaN
"NaN is a string" It prints as text NaN is a missing floating point value, so a column with NaN becomes float
"size and count() are equal" Both count values size includes NaN; count() excludes it
"drop('Maths') changes the DataFrame" It sounds in place It returns a new DataFrame; assign it back
"axis=0 drops columns" Axis 0 sounds like the first thing axis=0 is rows; axis=1 is columns
"A dictionary key becomes a row label in a DataFrame" It is in the first position Keys become column labels
"ResultDF['New'] = [1, 2] on 3 rows works" Python allows short lists elsewhere Values must match the number of rows, otherwise ValueError

60-second revision

Topic Remember
Libraries NumPy for arrays, Pandas for tables, Matplotlib for charts
Series 1-D, labelled index; default index 0 to N minus 1
Create Series List, ndarray, dictionary (keys become the index)
Slicing Position: end excluded. Label: end included
Series attributes name, index.name, values, size, empty
Series methods head, tail, count (non NaN)
Arithmetic Aligns on labels, NaN for unmatched; fill_value in add sub mul div
DataFrame 2-D; dictionary keys become columns
loc Label based; slices include the end
Add and delete df['col'] = ..., df.loc['row'] = ..., drop(label, axis=0/1)
axis 0 is rows, 1 is columns
CSV read_csv(sep, header, names); to_csv(sep, header, index)

Where this chapter connects

A Series is a labelled version of the array from Introduction to NumPy, and the idea of slicing, shape and element wise operations carries over directly. The next chapter, Data Handling using Pandas - II, adds descriptive statistics, sorting, grouping and more operations on DataFrames, and Plotting Data using Matplotlib turns these tables into charts. A DataFrame is also the Python version of the SQL tables you queried in Querying and SQL Functions.

🎯 Practice MCQs

First 3 questions free · create a free account to unlock the rest — answers & explanations included, no payment needed

Q1. Consider: (1) import pandas as pd (2) s = pd.Series([10, 20, 30]) (3) print(s[2]). What is printed?

▸ Show answer & explanation

Answer: B

With no index given, the labels are 0, 1 and 2, so the label 2 refers to the third value, 30. Option A is the value at label 1, and option C is the label, not the value.

Q2. A Series is created with pd.Series({'India': 'NewDelhi', 'UK': 'London', 'Japan': 'Tokyo'}). What forms the index of the Series?

▸ Show answer & explanation

Answer: C

When a Series is created from a dictionary, the dictionary keys become the index and the values become the data. The default 0, 1, 2 index (A) is used only when no labels are available.

Q3. Consider: (1) import numpy as np (2) arr = np.array([1, 2, 3, 4]) (3) s = pd.Series(arr, index=['Jan', 'Feb', 'Mar']). What happens?

▸ Show answer & explanation

Answer: C

The number of index labels must equal the number of values. Four values with three labels cause a ValueError. Pandas does not invent a fourth label (B) or drop data (A and D).

🔒 12 more practice MCQs

Create a free account to unlock every MCQ in this chapter — answers and explanations included. No payment needed.

Already registered? Just log in and they'll all appear here.

📊 Previous-Year Questions

Practise with real CUET Computer Science previous-year papers — every question solved, with the correct answer and a step-by-step explanation.

View solved CUET PYQ papers →

Ready to drill Computer Science?

Unlock all MCQs, chapter tests, mocks & PYQs for ₹199/year.

Get UniDrill Pro