The idea
A dataset usually has more columns and rows than any one question needs. Selecting just what you need makes output readable, calculations faster, and code clearer about what it actually uses. This module covers the everyday selections; the next two add filtering by condition and the precise loc/iloc tools.
import pandas as pd
sales = pd.read_csv("sales.csv")
One column: a Series
Square brackets with a single column name give you that column as a Series:
revenue = sales["revenue"]
print(type(revenue).__name__)
print(revenue.head(3))
print(revenue.sum())
Series
0 89000
1 285000
2 960000
Name: revenue, dtype: int64
17502000
You may also see sales.revenue (attribute style). It works for simple names, but breaks for names with spaces, names that clash with methods (a column called count or size), and new columns. Stick to sales["revenue"]: it always works.
Several columns: a DataFrame
Most reports need a few columns, not one. Say you want to see who bought what and for how much: that's the customer, product and revenue columns. Pass a list of column names inside the brackets, which is why you see double brackets:
lines = sales[["customer", "product", "revenue"]]
print(type(lines).__name__)
print(lines.head(3))
DataFrame
customer product revenue
0 Kartika Rahma Desk Organizer 89000
1 Citra Dewi Arabica Gayo 250g 285000
2 Eka Putri Pour-over Kettle 960000
The outer brackets select; the inner brackets are the list. The columns come back in the order you list them, so this is also how you reorder columns.
df["col"] gives a Series; df[["col"]] (a list with one name) gives a DataFrame with one column. Many "why doesn't this method work" puzzles come down to which one you have.
Dropping columns you don't want
When you want almost every column, listing them all is tedious. If you only want to get rid of a couple, it's easier to say what to remove:
slim = sales.drop(columns=["segment", "channel"])
print(slim.shape)
print(sales.shape)
(71, 10)
(71, 12)
.drop() returns a new DataFrame; sales is unchanged. Most pandas methods work this way, which keeps your original data safe. You'll meet more column operations in module 7.
Rows by position: slicing
Slicing with : inside the brackets selects rows by position, exactly like a list:
print(sales[["customer", "product"]][10:13])
customer product
10 Lukman Hakim Throw Pillow
11 Lukman Hakim Robusta Lampung 250g
12 Lukman Hakim Notebook A5 Dotted
Rows 10, 11 and 12: start included, stop excluded. .head(n) and .tail(n) are the readable shortcuts for the first and last rows.
The square-bracket rules are a little overloaded: a string or list selects columns, a slice selects rows. That's convenient for quick work, but it's also why the next modules introduce .loc and .iloc, which are always explicit.
Picking one value
To get a single value, select the column, then the row label:
print(sales["product"][2])
print(sales.loc[2, "product"])
Pour-over Kettle
Pour-over Kettle
Both work; .loc[row, column] is the cleaner, one-step form, and it's what you should use (module 6 explains why).
Selecting columns by type or name pattern
For wide tables, select columns by a rule rather than typing every name:
numbers = sales.select_dtypes(include="number")
print(list(numbers.columns))
price_cols = [col for col in sales.columns if "price" in col or col == "revenue"]
print(price_cols)
['order_id', 'quantity', 'unit_price', 'revenue']
['unit_price', 'revenue']
select_dtypes picks columns by type ("number", "object", "datetime"), and a plain list comprehension over df.columns handles any naming rule you can express in Python.
Sorting the columns for a report
Combining what you know: select, reorder, and show the most important rows.
report_cols = ["order_id", "order_date", "customer", "revenue"]
print(sales[report_cols].head(4).to_string(index=False))
order_id order_date customer revenue
1 2024-01-03 Kartika Rahma 89000
2 2024-01-09 Citra Dewi 285000
3 2024-01-17 Eka Putri 960000
4 2024-01-18 Citra Dewi 75000
.to_string(index=False) prints without the row labels, handy for clean text output. Keeping the column list in a named variable (report_cols) documents what the report contains and makes it easy to change.
Why this matters for clean code
Select early and explicitly. A function that only needs three columns should take or create a three-column frame, so a reader sees exactly what data it depends on, and changes to other columns can't break it. Name your column lists (ID_COLUMNS, report_cols), prefer df["col"] over attribute access, and remember that methods like .drop() return new frames rather than changing the original.
Recap
df["col"]→ Series;df[["a", "b"]]→ DataFrame with those columns, in that order.df.drop(columns=[...])returns a new frame without them.df[10:13]slices rows by position;.head()/.tail()are shortcuts.df.loc[row, "col"]gets one value.select_dtypes(include="number")and comprehensions overdf.columnsselect columns by rule..to_string(index=False)prints without the index.