BP101T — Basics of Python Programming for Pharmaceutical Sciences
2 hrs/week · 2 credits · 30 hours · 50 marks (20 internal + 30 end-semester). Notes follow the PCI B.Pharm (NEP 2020) Semester 1 syllabus, unit by unit.
Units in this subject
- Unit 1 — Introduction to Python Programming
- Unit 2 — Control Structures & Functions
- Unit 3 — Data Structures & File Handling
- Unit 4 — Data Handling with Pandas
- Unit 5 — Data Visualization with Matplotlib
Download BP101T PDF Notes
Free PDFs for this subject in three tiers:
These are exam-focused study notes for BP101T – Basics of Python Programming for Pharmaceutical Sciences (Theory), Semester I of B.Pharm (NEP 2020 / PCI 2026 syllabus). The paper is 50 marks (20 internal + 30 end-semester), with a 1.5-hour end-semester exam, taught at 2 hours per week.
How to use these notes: Each unit follows the syllabus topic-by-topic. Code examples are short on purpose — in the exam you will be asked to write small programs (dosage calculation, BMI) and explain concepts (IDEs, data structures, Pandas functions), not to build large software. Read the explanation, type out the code yourself once, and memorise the "likely exam angles" at the end of each unit.
Unit I — Introduction to Python Programming
1. Installing Python and an IDE
Python is a high-level, interpreted programming language. "Interpreted" means you don't compile the whole program first — Python reads and runs your code line by line. That makes it beginner-friendly and ideal for quick calculations and data analysis, which is exactly why it is taught in pharmacy.
Installing Python: Download the installer from the official website (python.org), run it, and make sure to tick "Add Python to PATH" during installation on Windows. Verify with:
python --version
An IDE (Integrated Development Environment) is software that combines a code editor with tools like a debugger, terminal, and package manager in one window. The syllabus names three:
- Jupyter Notebook — runs in your browser; code is executed in small blocks called cells. Best for data analysis because you can see output, tables and plots right below each cell. This is the one you will use most in Units III–V.
- PyCharm — a full-featured IDE made specifically for Python. Strong autocomplete, built-in debugger, project management. Heavier to install.
- VS Code (Visual Studio Code) — a lightweight editor with Python extensions. Fast, popular, good middle ground.
Advantages of IDEs over plain text editors (Notepad, Notepad++):
| IDE | Plain text editor |
|---|---|
| Syntax highlighting + autocomplete | No code assistance |
| Built-in debugger to find errors line by line | You debug by guessing |
| Integrated terminal and package installer | You switch between separate tools |
| Error warnings as you type | Errors appear only when you run the code |
Likely exam question: "What is an IDE? Explain the advantages of IDEs over text editors with examples." (3–5 marks)
2. Variables and data types
A variable is a named container that stores a value. Python is dynamically typed — you don't declare the type; Python figures it out from the value.
drug_name = "Paracetamol" # string
dose_mg = 500 # integer
half_life = 2.5 # float
is_otc = True # boolean
The four data types in the syllabus:
- int — whole numbers:
500,-20 - float — decimal numbers:
2.5,98.6 - str — text in quotes:
"Aspirin",'500mg' - bool —
TrueorFalse(note the capital letters)
Check a type with type(): type(dose_mg) gives <class 'int'>.
3. Type casting
Type casting means converting one data type into another. This matters because input() always gives you a string, and you can't do maths on strings.
weight = input("Enter weight in kg: ") # input() returns a string, e.g. "60"
weight = float(weight) # cast to float: 60.0
dose = weight * 10 # now maths works: 600.0
print("Dose =", dose, "mg")
Common casting functions: int(), float(), str(), bool(). Note that int(2.9) gives 2 (it truncates, doesn't round), and int("abc") throws an error.
4. Basic operators
Arithmetic operators:
| Operator | Meaning | Example | Result |
|---|---|---|---|
+ |
Addition | 5 + 2 |
7 |
- |
Subtraction | 5 - 2 |
3 |
* |
Multiplication | 5 * 2 |
10 |
/ |
Division (always float) | 5 / 2 |
2.5 |
// |
Floor division | 5 // 2 |
2 |
% |
Remainder (modulus) | 5 % 2 |
1 |
** |
Power | 2 ** 3 |
8 |
Pharma use: ** for BMI (height squared), % for checking even/odd batch numbers, // for full strips from total tablets.
Comparison operators (return True/False): == (equal), != (not equal), > , <, >=, <=. Never confuse = (assignment) with == (comparison) — a classic exam trap.
Logical operators: and, or, not.
age = 25
weight = 60
eligible = (age >= 18) and (weight >= 50) # True only if both are true
5. Input and output operations
- Output:
print()displays values. You can print multiple items separated by commas, and use f-strings for neat formatting:
drug = "Ibuprofen"
dose = 400
print(f"{drug} {dose}mg to be taken twice daily")
# Output: Ibuprofen 400mg to be taken twice daily
- Input:
input()reads a line of text from the user and always returns a string — cast it before calculating.
6. Basic string operations and manipulation
Strings support several operations:
s = " Paracetamol 500mg "
s.strip() # removes spaces at both ends -> 'Paracetamol 500mg'
s.upper() # ' PARACETAMOL 500MG '
s.lower() # ' paracetamol 500mg '
s.replace("500mg", "650mg") # replaces a part
"a,b,c".split(",") # ['a', 'b', 'c'] - splits into a list
"-".join(["2026", "09", "24"]) # '2026-09-24' - joins a list
len("Aspirin") # 7 - length of string
"Aspirin" + " 75mg" # concatenation -> 'Aspirin 75mg'
"Na" * 3 # repetition -> 'NaNaNa'
"para" in "paracetamol" # True - membership test
Indexing starts at 0: "Aspirin"[0] is 'A'. Slicing extracts parts: "Paracetamol"[0:4] is 'Para'.
Syllabus overlap note: "String manipulation techniques" appears in both Unit I and Unit III. Learn the methods once (above); in Unit III the focus shifts to using them while handling lists, dictionaries and file data.
7. Standard libraries vs third-party libraries
- Standard library — modules that come pre-installed with Python. You just
importthem:math(square roots, logarithms — handy for pH and half-life math),statistics(mean, median),datetime(dates),csv(reading files),random,os. - Third-party libraries — written by the community, installed separately with pip (Python's package installer):
pip install numpy
pip install pandas matplotlib
pip uninstall numpy # removes a library
pip list # shows installed libraries
Key pharma libraries you will meet in later units: NumPy (fast numerical arrays), Pandas (tables/datasets), Matplotlib (graphs). In the exam, remember: standard = comes with Python; third-party = installed via pip.
Unit I — Frequently asked questions
Q: What is the difference between an IDE and a text editor? A: An IDE bundles a code editor with a debugger, terminal, autocomplete and package tools in one application (e.g. Jupyter, PyCharm, VS Code). A text editor (e.g. Notepad) only lets you type and save text — no debugging, no code suggestions, no integrated run button.
Q: Why does input() need type casting?
A: Because input() always returns a string. "60" * 10 would repeat text, not multiply. Casting with int() or float() converts it to a number first.
Q: What is the difference between / and // in Python?
A: / is normal division and always returns a float (5 / 2 = 2.5). // is floor division — it drops the decimal part (5 // 2 = 2).
Q: How do I install a Python library?
A: With pip from the terminal: pip install library_name (e.g. pip install pandas). Remove with pip uninstall library_name.
Unit I — Key takeaways
- Python is interpreted and dynamically typed; install from python.org and tick "Add to PATH".
- Jupyter Notebook is best for data work (Units III–V); PyCharm and VS Code are general-purpose IDEs.
- IDEs beat text editors on debugging, autocomplete, and integrated tools — a favourite 5-mark question.
- Core types:
int,float,str,bool; cast withint(),float(),str(). input()returns a string — always cast before arithmetic.- Memorise the operator table, especially
=vs==and/vs//. - Standard libraries come with Python (
math,csv,statistics); third-party libraries are installed withpip(numpy,pandas,matplotlib).
Unit II — Control Structures & Functions
1. Conditional statements (if, if-else, if-elif-else)
Programs often need to make decisions. Conditional statements run different code depending on whether a condition is True or False. Python uses indentation (spaces at the start of a line) to mark which lines belong to the condition — there are no curly brackets like in other languages.
age = 20
if age >= 18:
print("Adult dose applicable")
else:
print("Paediatric dose required")
When there are more than two possibilities, use elif:
temp = 38.5
if temp < 37.5:
print("Normal")
elif temp < 39:
print("Mild fever - monitor")
else:
print("High fever - intervene")
Nested conditions are if-statements inside other if-statements:
age = 65
has_allergy = False
if age >= 60:
if has_allergy:
print("Use alternative drug")
else:
print("Reduce dose - elderly patient")
else:
print("Standard adult dose")
Exam tip: Examiners love asking you to "write a program to check whether a patient is eligible for a drug based on age and weight using nested if." Practise the pattern above.
2. Loops — for loop and while loop
Loops repeat a block of code. Use a for loop when you know how many times to repeat; use while when you repeat until a condition changes.
# for loop - iterate over a fixed sequence
drugs = ["Aspirin", "Paracetamol", "Ibuprofen"]
for drug in drugs:
print(drug)
for i in range(5): # 0, 1, 2, 3, 4
print("Tablet", i + 1)
range(n) generates numbers from 0 to n−1. range(1, 6) gives 1–5, and range(0, 24, 2) gives 0, 2, 4, … 22 (the third number is the step).
# while loop - repeats until the condition becomes False
count = 3
while count > 0:
print(f"{count} tablets remaining")
count = count - 1 # without this line the loop never ends!
Warning: a while loop whose condition never becomes False is an infinite loop — the program hangs. Always make sure something inside the loop moves the condition towards False.
3. break and continue
breakexits the loop immediately.continueskips the rest of the current round and jumps to the next round.
# Stop searching once the drug is found
stock = ["Aspirin", "Paracetamol", "Ibuprofen", "Amoxicillin"]
for item in stock:
if item == "Ibuprofen":
print("Found Ibuprofen - stopping search")
break
print("Checking:", item)
# Skip expired batches, process the rest
batches = ["B101", "B102-expired", "B103", "B104-expired"]
for batch in batches:
if "expired" in batch:
continue # skip this one
print("Processing batch:", batch)
Likely exam question: "Differentiate between break and continue with an example." (3 marks)
4. Defining and calling functions
A function is a reusable block of code with a name. You define it once with def and call it whenever needed.
def greet():
print("Welcome to PharmacoLearn")
greet() # calling the function
greet() # call it again - no need to rewrite
Passing arguments and returning values:
def calculate_dose(weight_kg, mg_per_kg):
dose = weight_kg * mg_per_kg
return dose # sends the result back to the caller
d = calculate_dose(60, 10) # arguments: 60 and 10
print("Dose:", d, "mg") # Output: Dose: 600 mg
- Arguments are the values you pass in (
60,10); parameters are the names in the definition (weight_kg,mg_per_kg). returngives a value back. A function withoutreturngives backNone.- You can set default arguments:
def calculate_dose(weight_kg, mg_per_kg=10):— thencalculate_dose(60)uses 10 automatically.
5. Modular programs for pharmaceutical applications
"Modular" simply means breaking a program into small functions, each doing one job. The syllabus asks for two classic examples — learn these by heart, they are the most probable programming questions in the paper.
Dosage calculation (Clark's rule): Child dose = Adult dose × (weight in kg ÷ 70)
def clarks_dose(adult_dose_mg, weight_kg):
"""Calculate paediatric dose using Clark's rule."""
return adult_dose_mg * (weight_kg / 70)
def main():
adult_dose = float(input("Enter adult dose (mg): "))
weight = float(input("Enter child weight (kg): "))
child_dose = clarks_dose(adult_dose, weight)
print(f"Child dose: {child_dose:.1f} mg")
main()
BMI calculation:
def calculate_bmi(weight_kg, height_m):
return weight_kg / (height_m ** 2)
def bmi_category(bmi):
if bmi < 18.5:
return "Underweight"
elif bmi < 25:
return "Normal weight"
elif bmi < 30:
return "Overweight"
else:
return "Obese"
weight = float(input("Enter weight (kg): "))
height = float(input("Enter height (m): "))
bmi = calculate_bmi(weight, height)
print(f"BMI: {bmi:.1f} - {bmi_category(bmi)}")
{bmi:.1f} formats the number to one decimal place. The BMI cut-offs (18.5 / 25 / 30) are standard WHO categories — worth memorising since pharmacology examiners like them.
Unit II — Frequently asked questions
Q: What is the difference between for loop and while loop in Python?
A: A for loop iterates over a known sequence (a list, or numbers from range()). A while loop repeats as long as a condition stays true, and is used when you don't know in advance how many rounds are needed — e.g. reading batches until stock runs out.
Q: How do you calculate BMI in a Python program?
A: BMI = weight in kg divided by height in metres squared: bmi = weight_kg / (height_m ** 2). Then classify with if-elif-else: below 18.5 underweight, 18.5–24.9 normal, 25–29.9 overweight, 30+ obese.
Q: What is Clark's rule in Python dosage programs?
A: Clark's rule estimates a child's dose from the adult dose using body weight: child_dose = adult_dose * (weight_kg / 70). In code, write it as a function so it can be reused.
Q: What does the return statement do in a function?
A: It sends a value back to wherever the function was called, and ends the function immediately. Without return, a function gives back None.
Unit II — Key takeaways
- Conditions:
if/elif/else; indentation (not brackets) defines blocks in Python. for= fixed repetitions withrange()or lists;while= repeat until a condition changes; avoid infinite loops.breakexits the loop;continueskips to the next round.- Functions:
def, arguments in /returnvalue out; default arguments save repetition. - Memorise the two modular programs: Clark's rule dosage and BMI with categories — highest probability coding questions.
- BMI cut-offs: 18.5 / 25 / 30.
Unit III — Data Structures & File Handling
1. Lists, tuples and dictionaries
These three structures store collections of data — for example, a list of drugs, or a record of one patient's details.
List — ordered, changeable (mutable), written in square brackets:
drugs = ["Aspirin", "Paracetamol", "Ibuprofen"]
drugs.append("Amoxicillin") # add at the end
drugs[1] = "Paracetamol 650mg" # change an item - allowed in lists
Tuple — ordered, unchangeable (immutable), written in round brackets:
tablet = ("Aspirin", 75, "mg") # name, strength, unit
# tablet[1] = 100 -> ERROR: tuples cannot be changed
Use a tuple when the data should never change accidentally — like a drug's fixed identity (name + strength).
Dictionary — stores key–value pairs, written in curly braces. Ideal for records:
patient = {"id": "P001", "age": 45, "drug": "Aspirin", "dose_mg": 75}
print(patient["drug"]) # Aspirin
patient["dose_mg"] = 150 # update a value
| Feature | List | Tuple | Dictionary |
|---|---|---|---|
| Brackets | [ ] |
( ) |
{ } |
| Ordered | Yes | Yes | Yes (insertion order) |
| Changeable | Yes | No | Yes (values) |
| Accessed by | Index number | Index number | Key name |
| Pharma example | List of drugs in stock | Fixed (name, strength) pair | One patient's record |
Likely exam question: "Differentiate between list, tuple and dictionary with examples." (5 marks — learn the table above.)
2. Indexing and slicing lists
Indexing picks one element. Counting starts at 0, and negative numbers count from the end:
drugs = ["Aspirin", "Paracetamol", "Ibuprofen", "Amoxicillin"]
drugs[0] # 'Aspirin' (first)
drugs[-1] # 'Amoxicillin' (last)
Slicing picks a range: list[start:stop] — start is included, stop is excluded:
drugs[0:2] # ['Aspirin', 'Paracetamol']
drugs[1:] # everything from index 1 onwards
drugs[:2] # everything before index 2
Basic operations on lists and dictionaries:
# Lists
stock = [120, 200, 150]
len(stock) # 3 - number of items
stock.append(90) # add one item
stock.remove(200) # remove by value
stock.sort() # sort in place
"Aspirin" in drugs # True - membership check
# Dictionaries
patient.keys() # all keys: id, age, drug, dose_mg
patient.values() # all values
patient.items() # key-value pairs
patient.get("allergy", "None") # safe lookup with a default
3. String manipulation techniques (applied)
You met string methods in Unit I. In data work they clean messy real-world text — drug names typed inconsistently in a dataset, for example:
raw = " ASPIRIN 75mg "
clean = raw.strip().lower() # 'aspirin 75mg'
clean.replace("75mg", "150mg") # 'aspirin 150mg'
"aspirin,paracetamol".split(",") # ['aspirin', 'paracetamol']
Typical exam framing: "Write a program to clean a list of drug names (remove spaces, convert to lowercase)."
4. Introduction to NumPy arrays
NumPy is the standard third-party library for fast numerical work. Its core object is the array — like a list, but every element must be the same type, and maths happens on all elements at once (no loops needed).
import numpy as np
conc = np.array([0, 5, 12, 20, 15, 8]) # drug concentrations
print(conc * 2) # [ 0 10 24 40 30 16] - element-wise, no loop!
print(conc.mean()) # 10.0
print(conc.max()) # 20
Array creation:
np.zeros(5) # [0. 0. 0. 0. 0.]
np.ones(4) # [1. 1. 1. 1.]
np.arange(0, 10, 2) # [0 2 4 6 8] - like range(), but an array
np.linspace(0, 1, 5) # [0. 0.25 0.5 0.75 1.] - evenly spaced points
Arithmetic operations are element-wise: a + b, a * 2, a ** 2 apply to every element. Useful attributes: arr.shape (dimensions), arr.dtype (data type), arr.size (element count).
NumPy vs Python lists (exam favourite): arrays are faster for large numerical data, use less memory, and support element-wise maths directly; lists are more flexible (mixed types) but need loops for calculations.
5. Reading and writing CSV files
A CSV (Comma-Separated Values) file is the simplest way to store tabular data — each line is a row, commas separate columns. Excel can open CSVs, and every dataset in Units III–V uses this format.
import csv
# Writing
with open("patients.csv", "w", newline="") as f:
writer = csv.writer(f)
writer.writerow(["PatientID", "Age", "Dose_mg"])
writer.writerow(["P001", 45, 500])
writer.writerow(["P002", 60, 250])
# Reading
with open("patients.csv", "r") as f:
reader = csv.DictReader(f) # each row becomes a dictionary
for row in reader:
print(row["PatientID"], row["Dose_mg"])
Notes: with open(...) closes the file automatically. newline="" prevents blank lines on Windows. DictReader is convenient because you access columns by name instead of index.
6. Understanding structured healthcare datasets
A structured dataset is data organised in rows and columns:
- Each row = one record (one patient, one tablet batch, one PK sample).
- Each column = one variable (age, dose, plasma concentration).
Example — a tiny pharmacokinetic (PK) dataset:
| Time_hr | Conc_ug_mL | Subject |
|---|---|---|
| 0 | 0 | S01 |
| 1 | 5.2 | S01 |
| 2 | 12.1 | S01 |
Before analysing any dataset, check: what does each column mean, what are the units, and are there missing or impossible values (negative age, blank dose)? That habit is what "understanding structured healthcare datasets" means in the exam.
7. Importing small pharmaceutical datasets — basic access and manipulation
Putting it together: load a small dataset and do basic tasks with it.
import csv
with open("dissolution.csv", "r") as f:
rows = list(csv.DictReader(f))
print("Batches:", len(rows)) # how many records
for r in rows:
if float(r["Dissolution_%"]) < 80: # basic filtering
print("Below spec:", r["BatchID"], r["Dissolution_%"])
Likely exam question: "Write a program to read a CSV file containing drug dissolution data and display the batches with dissolution below 80%."
Unit III — Frequently asked questions
Q: What is the difference between a list and a tuple in Python? A: Both are ordered collections accessed by index, but a list is mutable (you can change, add or remove items) while a tuple is immutable (fixed after creation). Use lists for things that change, like stock levels; tuples for fixed data, like a drug's (name, strength) pair.
Q: What is NumPy and why is it used in pharmaceutical data analysis? A: NumPy is a third-party library for fast numerical computing. Its array object performs element-wise maths without loops and uses less memory than lists, making it ideal for large numerical datasets like concentration–time data or dissolution readings.
Q: How do you read a CSV file in Python?
A: With the built-in csv module: open the file with with open("file.csv") as f, then use csv.DictReader(f) to read each row as a dictionary keyed by column name.
Q: What is a structured healthcare dataset? A: Data arranged in rows and columns where each row is one record (e.g. one patient) and each column is one variable (e.g. age, dose, concentration). CSV files are the most common format.
Unit III — Key takeaways
- List = mutable
[ ], tuple = immutable( ), dictionary = key–value{ }— learn the comparison table. - Indexing starts at 0; negative indexes count from the end; slicing is
[start:stop](stop excluded). - String methods (
strip,lower,split,replace) clean messy dataset text. - NumPy arrays enable element-wise maths without loops;
mean(),max(),arange(),linspace()are core. - CSV is the universal dataset format;
csv.writer/csv.DictReaderwithwith open(...)is the exam pattern. - Structured dataset = rows are records, columns are variables; always check units and missing values first.
Unit IV — Data Handling with Pandas
1. Introduction to the Pandas library
Pandas is the most important Python library for working with tabular data — patient records, PK study results, ADR (Adverse Drug Reaction) reports. While NumPy handles raw numbers, Pandas handles labelled tables: it knows column names, handles missing values gracefully, and makes filtering and grouping one-liners.
import pandas as pd # the universal alias - always import it this way
Install it with pip install pandas. Reading Excel files additionally needs pip install openpyxl.
2. Series and DataFrame structures
Pandas has two main structures:
- Series — a one-dimensional labelled array (one column of data):
doses = pd.Series([500, 250, 500, 750], index=["P001", "P002", "P003", "P004"])
print(doses["P002"]) # 250 - accessed by label, not position
- DataFrame — a two-dimensional table with rows and columns (the structure you will use 95% of the time):
data = {"PatientID": ["P001", "P002", "P003"],
"Age": [45, 60, 32],
"Dose_mg": [500, 250, 500]}
df = pd.DataFrame(data)
print(df)
Think of it as: a DataFrame is a table, and each column of that table is a Series.
3. Reading CSV and Excel files — PK study datasets and ADR reports
pk = pd.read_csv("pk_study.csv") # pharmacokinetic study data
adr = pd.read_excel("adr_reports.xlsx") # adverse drug reaction reports
read_csv() and read_excel() each return a DataFrame. Two practical points examiners like:
- The file path must be correct — keep your dataset in the same folder as your notebook to avoid path errors.
- Excel files need the
openpyxllibrary installed, otherwiseread_excel()fails.
4. Inspecting datasets — head(), tail(), info(), describe()
The first thing you do with any new dataset is look at it. These four functions are the standard inspection toolkit:
df.head() # first 5 rows - what do the columns look like?
df.tail() # last 5 rows - anything odd at the end?
df.info() # column names, data types, and non-missing counts
df.describe() # summary statistics: count, mean, std, min, max
head()/tail()take an optional number:df.head(10)shows 10 rows.info()is how you spot problems: a column that should be numbers but shows asobject(text) usually means dirty data.describe()only summarises numeric columns by default — that is normal, not an error.
Likely exam question: "Explain any four functions used to inspect a dataset in Pandas." — this is literally head(), tail(), info(), describe().
5. Data cleaning techniques and handling missing values
Real datasets have gaps — a missed blood sample, an unrecorded age. Missing values appear as NaN (Not a Number) in Pandas. You must handle them before analysis, or your means and plots will be wrong.
df.isnull().sum() # count missing values in each column
df.dropna() # remove rows containing any missing value
df.fillna(0) # replace missing values with 0
df["Age"].fillna(df["Age"].mean()) # replace with the column's mean
df.drop_duplicates() # remove duplicate rows
df["Dose_mg"] = df["Dose_mg"].astype(int) # fix a wrong data type
When to use which: dropna() when missing rows are few and unimportant; fillna() when you can't afford to lose rows (common in small clinical datasets — dropping 3 of 20 patients hurts). Filling with the mean is the textbook approach for numeric columns.
Other cleaning steps: fixing wrong data types with astype(), removing duplicates with drop_duplicates(), and standardising text with string methods (.str.lower(), .str.strip() work on whole columns).
6. Filtering and selecting data based on conditions
Filtering picks out rows that meet a condition, using boolean indexing:
adults = df[df["Age"] >= 18] # rows where Age >= 18
high_dose = df[(df["Dose_mg"] > 500) & (df["Age"] < 60)] # two conditions: & = AND
elderly_aspirin = df[(df["Drug"] == "Aspirin") | (df["Age"] > 65)] # | = OR
Note the parentheses — each condition needs its own pair of brackets, a very common source of errors. Use & for AND and | for OR (not the words and/or, which don't work here).
Selecting picks columns or specific cells:
df["Dose_mg"] # one column (a Series)
df[["Drug", "Dose_mg"]] # multiple columns (a DataFrame)
df.loc[0, "Drug"] # row labelled 0, column "Drug"
df.iloc[0:3, 0:2] # first 3 rows, first 2 columns (by position)
loc uses labels, iloc uses integer positions — another favourite exam distinction.
7. Grouping data and performing aggregation functions
Grouping splits the data by a category and then computes a summary for each group — e.g. average dose per drug, ADR count per drug:
df.groupby("Drug")["Dose_mg"].mean() # mean dose for each drug
df.groupby("Drug")["ADR_Reported"].sum() # total ADRs per drug
df.groupby("Drug").agg({"Dose_mg": "mean", # multiple summaries at once
"Age": "max",
"PatientID": "count"})
Common aggregation functions: mean(), sum(), count(), max(), min(), std().
Likely exam question: "A dataset contains ADR reports with columns Drug and ADR_Reported. Write Pandas code to find the total number of ADRs reported for each drug." Answer: df.groupby("Drug")["ADR_Reported"].sum().
Unit IV — Frequently asked questions
Q: What is the difference between a Pandas Series and a DataFrame? A: A Series is one-dimensional — a single labelled column of data. A DataFrame is two-dimensional — a full table of rows and columns. Each column of a DataFrame is a Series.
Q: How do you handle missing values in Pandas?
A: First find them with df.isnull().sum(). Then either remove rows with df.dropna() or fill gaps with df.fillna() — e.g. df["Age"].fillna(df["Age"].mean()) replaces missing ages with the column mean.
Q: What is the difference between loc and iloc in Pandas?
A: loc selects by row/column labels (e.g. df.loc[0, "Drug"]), while iloc selects by integer position (e.g. df.iloc[0:3, 0:2]).
Q: How do you read CSV and Excel files in Pandas?
A: pd.read_csv("file.csv") for CSV files and pd.read_excel("file.xlsx") for Excel files (which needs openpyxl installed). Both return a DataFrame.
Unit IV — Key takeaways
- Pandas = labelled tables; import as
pd; Series is 1-D, DataFrame is 2-D. - Read files with
read_csv()/read_excel()(Excel needsopenpyxl). - Inspect first, always:
head(),tail(),info(),describe(). - Missing values: find with
isnull().sum(); fix withdropna()orfillna()(mean-fill for numeric columns). - Filter rows with boolean indexing (
&= AND,|= OR, brackets around each condition); select withloc(labels) vsiloc(positions). - Summarise by category with
groupby()+mean()/sum()/count()/agg()— the standard route to "ADRs per drug" type questions.
Unit V — Data Visualization with Matplotlib
1. Introduction to Matplotlib
Matplotlib is Python's core plotting library — it turns your analysed data into graphs. In pharmacy, graphs aren't decoration: a concentration–time curve is the result of a PK study, and a dissolution profile is how you compare formulations.
import matplotlib.pyplot as plt # the standard alias
The basic pattern is always the same: create the plot, label it, then show it.
plt.plot([0, 1, 2, 3], [0, 5, 12, 8]) # x-values, y-values
plt.show() # display the figure
In Jupyter Notebook, plots appear directly below the cell, so plt.show() is optional but harmless.
2. Creating line plots, histograms, scatter plots and box plots
Line plot — shows a trend over a continuous variable (usually time). The workhorse of pharma graphs.
time = [0, 1, 2, 4, 8, 12]
conc = [0, 5, 12, 18, 10, 4]
plt.plot(time, conc)
plt.show()
Histogram — shows the distribution of one variable: how values spread out. Example: weights of 100 tablets — you want them clustered tightly around the target weight.
weights = [498, 500, 501, 499, 502, 500, 497, 501, 500, 499]
plt.hist(weights, bins=5)
plt.show()
bins controls how many bars the range is split into. Too few bins hides detail; too many makes noise.
Scatter plot — shows the relationship between two variables, one dot per observation. Example: dose vs percentage pain relief.
dose = [100, 200, 300, 400, 500]
relief = [20, 35, 55, 70, 85]
plt.scatter(dose, relief)
plt.show()
If the dots trend upward, the variables are positively related — the visual basis of dose–response.
Box plot — compares the distribution of a variable across groups in one view. Example: dissolution percentages for three formulations side by side.
f1 = [82, 85, 88, 84, 86]
f2 = [70, 72, 68, 75, 71]
f3 = [90, 92, 89, 91, 93]
plt.boxplot([f1, f2, f3], labels=["F1", "F2", "F3"])
plt.show()
Each box shows the median (line inside), the middle 50% of values (the box), and the overall spread (the whiskers). Dots outside the whiskers are possible outliers.
3. Labelling axes, titles and legends
An unlabeled graph earns no marks — in the exam or in a real report. Always label:
plt.plot(time, conc, label="Formulation A") # label= names the line
plt.plot(time, conc2, label="Formulation B")
plt.xlabel("Time (hours)") # x-axis with units
plt.ylabel("Plasma conc. (µg/mL)") # y-axis with units
plt.title("Dissolution profile comparison")
plt.legend() # shows the label= names
plt.grid(True) # light grid - easier to read values
plt.show()
Checklist before you finish any plot: x-label with units, y-label with units, title, legend (when more than one series), grid.
4. Visualizing pharmaceutical datasets
Concentration–time curves: oral vs IV administration (line plots):
time = [0, 1, 2, 4, 8, 12]
conc_oral = [0, 5, 12, 18, 10, 4] # rises to a peak, then falls
conc_iv = [25, 18, 12, 7, 3, 1] # starts highest, falls immediately
plt.plot(time, conc_oral, marker="o", label="Oral")
plt.plot(time, conc_iv, marker="s", label="IV bolus")
plt.xlabel("Time (hours)")
plt.ylabel("Plasma concentration (µg/mL)")
plt.title("Concentration–time curves: oral vs IV")
plt.legend()
plt.grid(True)
plt.show()
What to read from it: the oral curve rises first (absorption phase) to a peak — the peak height is Cmax, the time it occurs is Tmax — then declines (elimination). The IV bolus curve starts at its maximum at time zero (no absorption) and only declines. The area under either curve relates to total drug exposure (AUC).
ADR reporting rates across drugs — a bar chart is the natural choice here (one bar per drug):
drugs = ["Aspirin", "Ibuprofen", "Paracetamol"]
adr_counts = [45, 30, 12]
plt.bar(drugs, adr_counts)
plt.xlabel("Drug")
plt.ylabel("Number of ADR reports")
plt.title("ADR reporting rates across drugs")
plt.show()
Syllabus note: Unit V lists line plots, histograms, scatter plots and box plots as the plot types to learn. Bar charts (used above for ADR rates) are the standard real-world choice for comparing counts across categories like drugs — worth knowing even though the syllabus names the other four.
Dissolution profiles — percentage of drug dissolved vs time, one line per formulation, exactly like the labelled example in section 3. This is how you visually compare a test formulation against a reference.
5. Scientific interpretation of plots
Examiners don't just want the code — they want you to read the graph. Practise these interpretations:
- Concentration–time curve: Identify Cmax and Tmax on the oral curve; explain why the IV curve has no absorption phase; state which formulation gives higher/faster exposure.
- Histogram (tablet weights): A narrow, symmetric histogram around the target weight means good content uniformity; a wide or skewed one signals a manufacturing problem.
- Scatter plot (dose vs response): An upward trend supports a dose–response relationship; scattered dots with no pattern mean no clear relationship.
- Box plot (formulations): Compare medians (which formulation dissolves most?), box heights (which is most consistent?), and outliers (any batch to investigate?).
Likely exam question: "Plot the given concentration–time data for oral and IV administration using Matplotlib and interpret the curves." (5 marks — code + two or three interpretation lines.)
Unit V — Frequently asked questions
Q: Which plot is used for concentration–time curves in Python?
A: A line plot (plt.plot()), with time on the x-axis and plasma concentration on the y-axis. Plot oral and IV data as two labelled lines with a legend.
Q: What is the difference between a histogram and a bar chart? A: A histogram shows the distribution of one continuous variable (e.g. tablet weights split into bins). A bar chart compares values across categories (e.g. ADR counts per drug). They look similar but answer different questions.
Q: How do you label a Matplotlib plot?
A: plt.xlabel() and plt.ylabel() for the axes (always include units), plt.title() for the title, and plt.legend() to identify multiple series labelled with label= in the plot commands.
Q: How do you read a box plot? A: The line inside the box is the median, the box covers the middle 50% of values, the whiskers show the overall spread, and dots beyond the whiskers are potential outliers.
Unit V — Key takeaways
- Matplotlib pattern: plot → label →
show(); import asplt. - Line plot = trends over time (concentration–time, dissolution); histogram = distribution (tablet weights); scatter = relationship between two variables (dose–response); box plot = compare groups (formulations).
- Every plot needs: x-label with units, y-label with units, title, legend for multiple series.
- Oral concentration–time curves show absorption (rise to Cmax at Tmax) then elimination; IV bolus starts at maximum and only declines.
- Interpretation matters as much as code: state what Cmax/Tmax, spread, trend or median differences mean scientifically.
- Bar charts are the standard choice for counts across categories (ADR rates per drug).
Quick revision checklist (all units)
- [ ] Unit I: IDE advantages, 4 data types, type casting, operator table, string methods, pip install/uninstall
- [ ] Unit II: if/elif/else, for vs while, break vs continue, function definition with
return, Clark's rule program, BMI program - [ ] Unit III: list vs tuple vs dictionary table, indexing/slicing, NumPy array creation and element-wise maths, CSV read/write pattern
- [ ] Unit IV: Series vs DataFrame,
read_csv/read_excel,head/tail/info/describe,dropna/fillna, boolean filtering,groupby+ aggregation,locvsiloc - [ ] Unit V: four plot types and when to use each, labelling checklist, reading Cmax/Tmax, interpreting histograms, scatter plots and box plots
Good luck — write the code by hand at least once per unit, and the 30-mark paper becomes very manageable.
Other Semester 1 subjects: BP101T · BP102T · BP103T · BP104T · BP105T · BP106T · Semester 1 Hub · MCQ Bank
