Lecture 2: Variables, Types and Operators

BAA1118 - Foundations of Programming for Analytics & AI

Damien Dupré

For Today

Requirements

  • ✅ A Google account and a Colab notebook that opens
  • ✅ You have run print() at least once
  • ✅ You have seen a NameError and survived

Programme

  1. Objects and assignment
  2. Numbers
  3. Strings
  4. Booleans
  5. Converting between types

Where We Are Going

Everything in this module is built on one idea:

Data is stored in objects. Objects have a type. The type decides what you are allowed to do.

A DataFrame in week 7 is just a very large object with a very particular type. Today is the foundation for all of it.

1. Objects and Assignment

Assigning a Value

revenue = 1500
  • revenue is the name
  • 1500 is the value
  • = is the assignment operator

Read it right to left: “take 1500, and label it revenue”.

= is not the mathematical equals. Equality is ==, which we will see later today.

Using a Name

Once assigned, the name can be used anywhere the value could be used.

revenue = 1500
costs = 900

profit = revenue - costs
print(profit)
600

The value is looked up at the moment the line runs:

revenue = 2000
print(profit)
600

profit does not update. It was computed once, and stored.

Reassigning

A name can be reused. The old value is simply forgotten.

counter = 1
print(counter)

counter = counter + 1
print(counter)
1
2

counter = counter + 1 looks like nonsense in maths and is completely normal in code. The right-hand side is computed first, then stored under the same name.

There is a shortcut:

counter += 1
print(counter)
3

Naming Rules

Python requires:

  • Letters, digits and underscores only
  • Cannot start with a digit: 2024_sales is invalid, sales_2024 is fine
  • No spaces, no dashes, no accents
  • Case sensitive: Sales, sales and SALES are three names
  • Cannot be a reserved word

Reserved Words

These 35 words belong to Python. Using one as a name is an error:

False None True and as assert async await break class continue def del elif else except finally for from global if import in is lambda nonlocal not or pass raise return try while with yield

The dangerous ones in analytics are sum, list, type, max, min, id and str. They are not reserved, but they are existing function names, and overwriting them breaks things quietly.

Naming Conventions

Python convention (not enforced, but universal):

  • snake_case for variables and functions: total_revenue, mean_price
  • Short but descriptive: df is fine for a working DataFrame, x1 is not fine for revenue
  • Nouns for data, verbs for functions: sales_2024, compute_margin()
a = 143.5     # what is this?
avg_basket_eur = 143.5   # ah.

Naming is not decoration. It is 90% of what makes code readable.

Your Turn: Naming

Try storing a value with the following names. Which of these are valid?

1st_quarter
first_quarter
FirstQuarter
first quarter
first-quarter
class
customer_id
c
_internal

2. Types

Every Object Has a Type

The function type() tells you what you are holding.

print(type(42))
print(type(4.2))
print(type("42"))
print(type(True))
print(type(None))
<class 'int'>
<class 'float'>
<class 'str'>
<class 'bool'>
<class 'NoneType'>

The Five Basic Types

Type Name Example Used for
int integer 42 counts, years, ids
float decimal 4.2 prices, measurements
str string "forty two" text, labels, categories
bool boolean True yes/no, filters
NoneType nothing None missing, not set yet

Next week adds list, dict, tuple and set. Week 7 adds Series and DataFrame.

Type Decides What Works

2 + 3
5
"2" + "3"
'23'
2 + "3"
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[10], line 1
----> 1 2 + "3"

TypeError: unsupported operand type(s) for +: 'int' and 'str'

Same operator +, three different behaviours. Read the type before blaming the operator.

3. Numbers

Arithmetic Operators

print(7 + 2)    # addition
print(7 - 2)    # subtraction
print(7 * 2)    # multiplication
print(7 / 2)    # division, always returns a float
9
5
14
3.5
print(7 // 2)   # floor division: how many whole 2s in 7
print(7 % 2)    # modulo: the remainder
print(7 ** 2)   # power
3
1
49

Order of Operations

Python follows normal mathematical precedence: ** first, then * / // %, then + -.

print(2 + 3 * 4)
print((2 + 3) * 4)
14
20

Tip

Use brackets even when they are unnecessary. (revenue - costs) / revenue costs nothing and removes all doubt.

int and float

print(type(10))
print(type(10.0))
print(type(10 / 2))
<class 'int'>
<class 'float'>
<class 'float'>

Division always produces a float, even when it divides evenly. This surprises everyone once.

print(10 / 2)
5.0

Rounding

price = 19.4567
print(round(price))       # to the nearest whole number
print(round(price, 2))    # to 2 decimals
print(round(price, 1))
19
19.46
19.5
print(abs(-15))     # absolute value
print(max(3, 9, 2)) # largest
print(min(3, 9, 2)) # smallest
15
9
2

Your Turn: A VAT Calculator

An invoice has a net amount of €1,847.35. Irish standard VAT is 23%.

  1. Store the net amount and the VAT rate in two well named variables
  2. Compute the VAT amount, rounded to 2 decimals
  3. Compute the gross amount
  4. Print the three values on three lines
  5. Check: does net + vat == gross return True? Why might it not?

Solution

net_eur = 1847.35
vat_rate = 0.23

vat_eur = round(net_eur * vat_rate, 2)
gross_eur = round(net_eur + vat_eur, 2)

print("Net:", net_eur)
print("VAT:", vat_eur)
print("Gross:", gross_eur)

print(net_eur + vat_eur == gross_eur)
Net: 1847.35
VAT: 424.89
Gross: 2272.24
True

4. Strings

Creating Strings

Single or double quotes, as long as they match:

name = "Damien"
module = 'BAA1118'
print(name, module)
Damien BAA1118

Use the other kind of quote when your text contains one:

print("It's a string")
print('She said "hello"')
It's a string
She said "hello"

Triple quotes span multiple lines:

print("""Line one
Line two""")
Line one
Line two

Numbers That Are Not Numbers

year_text = "2024"
print(type(year_text))
<class 'str'>
year_text + 1
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[23], line 1
----> 1 year_text + 1

TypeError: can only concatenate str (not "int") to str

This is the single most common error when reading data from a file: the column looks numeric, and is stored as text. We will meet it again in week 7.

Joining Strings

first = "Damien"
last = "Dupré"
print(first + " " + last)
Damien Dupré

Repetition works too:

print("-" * 40)
----------------------------------------

f-strings

Concatenating with + becomes painful fast. f-strings are the modern solution:

units = 340
price = 12.99
revenue = units * price

print(f"Selling {units} units at {price} euros gives {revenue} euros")
Selling 340 units at 12.99 euros gives 4416.6 euros
  • The f before the quote turns on the feature
  • Anything inside {} is Python code, evaluated and inserted

Formatting Inside f-strings

revenue = 4416.6

print(f"Revenue: {revenue:.2f}")      # 2 decimals
print(f"Revenue: {revenue:,.2f}")     # thousands separator
print(f"Revenue: {revenue:10.2f}|")   # padded to width 10
print(f"Share: {0.2534:.1%}")         # as a percentage
Revenue: 4416.60
Revenue: 4,416.60
Revenue:    4416.60|
Share: 25.3%

This is how every number in a report should be printed.

String Methods

A method is a function attached to an object, called with a dot.

country = "  ireland  "

print(country.strip())          # remove surrounding spaces
print(country.strip().title())  # and capitalise
print(country.upper())
print(country.replace("ireland", "Éire"))
ireland
Ireland
  IRELAND  
  Éire  

Methods return a new string. The original is unchanged:

print(country)
  ireland  

More String Methods

sentence = "revenue,costs,profit"

print(sentence.split(","))          # into a list
print(sentence.startswith("rev"))
print("costs" in sentence)          # membership test
print(len(sentence))                # number of characters
['revenue', 'costs', 'profit']
True
True
20

Data cleaning in week 7 is mostly these five methods applied to a whole column at once.

Indexing a String

Python counts from zero.

word = "Python"
print(word[0])    # first character
print(word[1])
print(word[-1])   # last character
print(word[0:3])  # from 0 up to, not including, 3
P
y
n
Pyt

Important

Zero-indexing and “up to but not including” apply everywhere in Python: strings, lists, DataFrames. Learn it once, this week.

Your Turn: Cleaning a Name

Somebody typed their university into a web form. This is what came back:

raw = "   dublin city UNIVERSITY  "

Tidy it up, in four steps:

  1. Copy that line into a cell, so raw holds the messy value
  2. Make a tidy version called clean: no spaces at the start or end, and each word starting with a capital. Two methods, one after the other.
  3. Print how many characters are in raw, then how many are in clean
  4. Print two more lines, exactly like the ones below
27 22
Cleaned '   dublin city UNIVERSITY  ' into 'Dublin City University'
True

The last line answers: is "City" inside clean?

Solution

raw = "   dublin city UNIVERSITY  "
clean = raw.strip().title()

print(len(raw), len(clean))
print(f"Cleaned '{raw}' into '{clean}'")
print("City" in clean)
27 22
Cleaned '   dublin city UNIVERSITY  ' into 'Dublin City University'
True

5. Booleans

True and False

print(type(True))
print(True, False)
<class 'bool'>
True False

Booleans are produced by comparisons:

print(10 > 5)
print(10 == 5)
print(10 != 5)
True
False
True

Comparison Operators

Operator Meaning
== equal to
!= not equal to
< > less than, greater than
<= >= less or equal, greater or equal
in is contained in

Warning

= assigns, == compares. Writing if x = 5 is a SyntaxError, and it happens to everyone.

Comparing Strings

print("dublin" == "Dublin")
print("dublin".lower() == "Dublin".lower())
print("a" < "b")
False
True
True

String comparison is alphabetical, and case matters. Lowercase everything before comparing text from real data.

Combining Conditions

age = 25
income = 48000

print(age > 18 and income > 30000)
print(age > 65 or income > 30000)
print(not age > 65)
True
True
True
  • and: both must be true
  • or: at least one must be true
  • not: flips the answer

Python uses the English words, not && and ||. Week 7 will use & and | for columns, which is a different thing.

Booleans Are Numbers

print(True + True)
print(sum([True, False, True, True]))
2
3

This looks like a curiosity. It is actually how you count things in data analysis:

# how many customers spent more than 100 euros?
(spending > 100).sum()

6. Converting Types

Casting

print(int("42"))
print(float("3.14"))
print(str(42))
print(bool(0), bool(1), bool(""))
42
3.14
42
False True False
print(int(9.99))    # truncates, does not round
print(round(9.99))
9
10

When Casting Fails

int("forty two")
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[40], line 1
----> 1 int("forty two")

ValueError: invalid literal for int() with base 10: 'forty two'
int("42.5")
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[41], line 1
----> 1 int("42.5")

ValueError: invalid literal for int() with base 10: '42.5'

The second one is a classic: int() will not go via float. Use int(float("42.5")).

input()

input() asks the user for a value, and always returns a string.

age = input("How old are you? ")
print(type(age))        # str, even if they typed 25

age = int(input("How old are you? "))
print(age + 1)

We will not use input() much: notebooks and data files replace it. But knowing it returns text explains a lot of beginner bugs.

Your Turn: Putting It Together

A survey exported these three values, all as text:

respondent = "  R_0042 "
age_text = "34"
spend_text = "128.75"
  1. Clean the respondent id (no spaces, uppercase)
  2. Convert the age and the spend to the right numeric types
  3. Compute the spend per year of age
  4. Print, with an f-string and 2 decimals: R_0042 (34) spends 3.79 per year of age
  5. Print True if the respondent spent more than 100 and is under 40

Solution

respondent = "  R_0042 "
age_text = "34"
spend_text = "128.75"

rid = respondent.strip().upper()
age = int(age_text)
spend = float(spend_text)

ratio = spend / age
print(f"{rid} ({age}) spends {ratio:.2f} per year of age")
print(spend > 100 and age < 40)
R_0042 (34) spends 3.79 per year of age
True

Thanks for your attention and don’t hesitate to ask if you have any questions!

@damien_dupre

@damien-dupre

https://damien-dupre.github.io

damien.dupre@dcu.ie