Read a CSV into a DataFrame with pd.read_csv, parse the dates, and index the rows by date.
A CSV file is a text file with one row per line and commas between the fields. pd.read_csv turns one into a DataFrame.
In this lesson I read prices.csv, a simulated file of daily closes sitting next to this page, check what came in, fix the date column, and pull out one ticker with its rows labelled by date.
Step 1. Read the file
The file has four columns, Date,Ticker,Close,Volume, and three tickers stacked on top of each other. Pass the filename and pandas hands back a DataFrame.
import pandas as pdprices = pd.read_csv("prices.csv") # the file sits in the same folder as this lessonprint(prices.head()) # first five rows, to see what came in# -> Date Ticker Close Volume# -> 0 2026-01-02 AAA 186.66 2183494# -> 1 2026-01-02 CCC 416.98 3117814# -> 2 2026-01-02 DDD 125.18 7447373# -> 3 2026-01-05 AAA 186.38 8463199# -> 4 2026-01-05 CCC 423.19 3345056print(prices.shape) # -> (120, 4)
head() shows the first five rows. shape is rows then columns: 120 rows, 4 columns. Three tickers times 40 business days gives 120.
The first line of the file became the column names, and the numbers on the left, 0 to 4, are the index pandas made up because I gave it nothing to use.
Step 2. Date arrives as text
read_csv guesses a type per column. Ask it what it decided.
print(prices.dtypes)# -> Date object# -> Ticker object# -> Close float64# -> Volume int64# -> dtype: object
Date object
Ticker object
Close float64
Volume int64
dtype: object
Close came in as a float and Volume as an integer. Date came in as object, which is how pandas reports a column of text. "2026-01-02" is a string of ten characters, not a date, so the date tools are not available on it.
print(type(prices["Date"][0])) # -> <class 'str'>
<class 'str'>
Pass parse_dates with a list of column names and pandas converts them while reading.
prices = pd.read_csv("prices.csv", parse_dates=["Date"]) # Date becomes a real timestampprint(prices.dtypes)# -> Date datetime64[ns]# -> Ticker object# -> Close float64# -> Volume int64# -> dtype: objectprint(prices["Date"].min(), "to", prices["Date"].max())# -> 2026-01-02 00:00:00 to 2026-02-26 00:00:00
Date datetime64[ns]
Ticker object
Close float64
Volume int64
dtype: object
2026-01-02 00:00:00 to 2026-02-26 00:00:00
datetime64 is a real timestamp. Now .dt reaches into it and pulls out the parts of the date.
print(prices["Date"].dt.day_name().head(3).tolist()) # .dt only works on a timestamp column# -> ['Friday', 'Friday', 'Friday']
['Friday', 'Friday', 'Friday']
The first three rows are the same day, one per ticker.
Step 3. Look at the whole frame
info() gives the row count, the column names, how many non-missing values each column holds, and its type, in one go.
Date left the columns and became the row labels. Ask for a day by name:
print(aapl.loc["2026-01-06", "Close"]) # -> 188.51 <- one cell, row label then columnprint(round(aapl["Close"].mean(), 2)) # -> 183.69 <- average close over the 40 days
188.51
183.69
read_csv can set the index while it reads, with index_col:
alt = pd.read_csv("prices.csv", parse_dates=["Date"], index_col="Date") # parsed and indexed in one readprint(alt.head(3))# -> Ticker Close Volume# -> Date# -> 2026-01-02 AAA 186.66 2183494# -> 2026-01-02 CCC 416.98 3117814# -> 2026-01-02 DDD 125.18 7447373
Ticker Close Volume
Date
2026-01-02 AAA 186.66 2183494
2026-01-02 CCC 416.98 3117814
2026-01-02 DDD 125.18 7447373
All three tickers are still in there, so every date appears three times.
So the mask still has to run. Same two lines again, on CCC:
msft = alt[alt["Ticker"] =="CCC"] # a date index does not filter, the mask still doesprint(msft.shape) # -> (40, 3)print(msft["Close"].head(3))# -> Date# -> 2026-01-02 416.98# -> 2026-01-05 423.19# -> 2026-01-06 423.95# -> Name: Close, dtype: float64
idxmax() gives the index label of the largest value, which is a date now that the date is the index.
NoteWhere does pandas look for the file?
pd.read_csv("prices.csv") looks in the working directory, which for a lesson page is the folder the page lives in. If the file sits somewhere else, give the path: pd.read_csv("data/prices.csv"), or the full path. A missing file raises FileNotFoundError naming what it tried to open.