EDA and Feature Engineeringpandas 3.0 · scikit-learn 1.9 · seaborn 0.13 · Python 3.12+
Dashboard
0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
72 small wins to finish your pathNext lesson →

Splitting a date into columns

A derived feature is a new column computed from one that a model cannot read, such as the three numbers day, month and year taken out of the text '24/03/2019'.

Last updated: 12 Oct, 2026 · pandas 3.0 · scikit-learn 1.9 · openpyxl 3.1

Box-Cox and Yeo-Johnson finished the work on columns that were already numbers. This part is about the columns that are not. The flight price dataset is nine of them in a row: a date, two times, a duration, a route and four categories, none of which a model can take as it stands.

Why a date has to become numeric columns · from the EDA and Feature Engineering video · 2:00:47 to 2:04:10

Loading the two Excel files

The dataset ships as two .xlsx files, a training set with the price and a test set without it. pd.read_excel reads them, which needs the openpyxl package installed; pd.concat stacks them into one frame so every column is engineered once rather than twice.

python
import pandas as pd

BASE = ("https://raw.githubusercontent.com/krishnaik06/5-Days-Live-EDA-and-Featur"
        "e-Engineering/main/Flight%20Prediction/")
train = pd.read_excel(BASE + "Data_Train.xlsx")   # 10683 rows, Price included
test = pd.read_excel(BASE + "Test_set.xlsx")      # 2671 rows, no Price
flights = pd.concat([train, test], ignore_index=True)

ignore_index=True renumbers the rows 0 to 13353. Without it the test rows keep their own 0 to 2670, the frame holds two rows labelled 0, and a later drop(6474) would delete every row carrying that label.

Looking at the dataset

ExampleThe flight price data, loaded from GitHub on pandas 3.0.6
import pandas as pd

BASE = ("https://raw.githubusercontent.com/krishnaik06/5-Days-Live-EDA-and-Featur"
        "e-Engineering/main/Flight%20Prediction/")
train = pd.read_excel(BASE + "Data_Train.xlsx")   # 10683 rows, Price included
test = pd.read_excel(BASE + "Test_set.xlsx")      # 2671 rows, no Price
flights = pd.concat([train, test], ignore_index=True)

print("train", train.shape, " test", test.shape, " together", flights.shape)
print(flights.dtypes.to_string())
print("\nnulls:", flights.isnull().sum()[lambda s: s > 0].to_dict())
  • Ten of the eleven columns are text. Only Price arrives as a number, and it is the target, not an input.
  • The test rows have no Price, so pd.concat fills those 2671 values with NaN. That is how to tell the two halves apart later.
  • Route and Total_Stops each have one null, and it is the same row.

Splitting the date string with .str

Date_of_Journey holds values such as 24/03/2019 and 1/05/2019. The day is everything before the first slash, the month is between the slashes and the year is after the second one.

Splitting the date with .str.split · from the EDA and Feature Engineering video · 2:04:15 to 2:08:10

Splitting one text column into a list

.str is the accessor that applies a Python string method to every value in a Series. .str.split('/') turns each value into a list of pieces, and .str[0] takes the first piece of each list.

python
day = flights['Date_of_Journey'].str.split('/').str[0]      # '24', '1', '9'
month = flights['Date_of_Journey'].str.split('/').str[1]    # '03', '05', '06'
year = flights['Date_of_Journey'].str.split('/').str[2]     # '2019' for every row

Casting the pieces to integers

Each piece is still text, so '1' and '01' are different values and nothing can be compared or subtracted. astype(int) makes them numbers.

python
flights['Date'] = flights['Date_of_Journey'].str.split('/').str[0].astype(int)
flights['Month'] = flights['Date_of_Journey'].str.split('/').str[1].astype(int)
flights['Year'] = flights['Date_of_Journey'].str.split('/').str[2].astype(int)

Dropping the column the pieces came from

Once the three numbers exist, the text column carries nothing new and would have to be encoded as 13354 separate categories. It goes.

python
flights = flights.drop(columns='Date_of_Journey')

Turning one date column into three

ExampleThe video's split, run on pandas 3.0.6
day = flights['Date_of_Journey'].str.split('/')
print("the split pieces:", day.head(3).tolist())

flights['Date'] = day.str[0].astype(int)
flights['Month'] = day.str[1].astype(int)
flights['Year'] = day.str[2].astype(int)
flights = flights.drop(columns='Date_of_Journey')

print(flights[['Date', 'Month', 'Year']].head(3).to_string())
print("\ndtypes:", flights[["Date", "Month", "Year"]].dtypes.tolist())
print("Year values:", flights["Year"].unique().tolist())
print("Month values:", sorted(flights["Month"].unique().tolist()))
  • The split pieces are lists such as ['24', '03', '2019'], one per row, which is why a second .str[i] is needed to reach inside them.
  • All three columns are int64 after the cast. Before it they were text, and '1' would never have equalled 1.
  • Year holds only 2019. A column with one value tells a model nothing and is the first thing the feature selection part throws away.
  • Month runs from 3 to 6. The data covers four months, so a month number is a usable feature here and would not be across several years.

The same split with to_datetime

The video splits the string because the split is what it is teaching. pandas also has a date type, and converting to it first gives the same three numbers plus everything else a date knows: the weekday, the week of the year, whether it is a weekend. The page shows both because the .str technique is needed for the time and duration columns, where no date type helps.

python
# format='%d/%m/%Y' says day first, then month, then year, separated by slashes
when = pd.to_datetime(flights['Date_of_Journey'], format='%d/%m/%Y')
flights['Date'] = when.dt.day
flights['Month'] = when.dt.month
flights['Weekday'] = when.dt.weekday      # Monday is 0, Sunday is 6
Exampleto_datetime next to the split, run on pandas 3.0.6
import matplotlib.pyplot as plt

when = pd.to_datetime(flights['Date_of_Journey'], format='%d/%m/%Y')
by_split = flights['Date_of_Journey'].str.split('/').str[0].astype(int)

print("to_datetime agrees with the split:", bool((when.dt.day == by_split).all()))
print("first three dates :", when.head(3).dt.strftime("%Y-%m-%d").tolist())
print("weekday names     :", when.dt.day_name().head(3).tolist())
print("flights per weekday:", when.dt.day_name().value_counts().to_dict())

per_month = when.dt.month.value_counts().sort_index()
fig, ax = plt.subplots(figsize=(6.5, 3.4))
ax.bar(per_month.index, per_month.values, color='#3a6fd8')
ax.set_xticks(per_month.index)
ax.set_xticklabels(['March', 'April', 'May', 'June'])
ax.set_ylabel('flights'); ax.set_title('Flights per month of journey')
fig.tight_layout()
A bar chart of flights per month of journey: March about 3400, April about 1300, May about 4300 and June about 4300.
  • The two routes agree on every row, so nothing is lost by using the date type.
  • day_name() is free once the column is a date, and it is a real feature: fares differ between a Wednesday and a Sunday. The split can never produce it.
  • The four months are uneven. April has far fewer flights than the other three, which is worth knowing before a model reads Month as a number.

Splitting the text compared with converting to a date

.str.splitpd.to_datetime
Givesexactly the pieces you ask forday, month, year, weekday, quarter, week, and more
Needsnothing, it is string worka format string, or the parsing is slow and can guess wrong
Catches a bad valueno, it produces a wrong piece in silenceyes, it raises unless errors='coerce'
Works onany text pattern, including a durationdates and times only
Use it fora column no date type understandsa column that holds a date

Where a derived date feature is used

  • Price and demand models. Day of the month, month and weekday all move an air fare, and the raw date string moves nothing.
  • Anything with a seasonal pattern, such as retail sales, where the month is the feature that carries Diwali or Christmas.
  • Age style features. Subtracting a date from today gives the age of a customer, a car or an account, which is usually stronger than the date itself.
Watch out. pd.to_datetime without a format argument has to guess, and on a column holding 1/05/2019 it may read the first number as a month. Pass the format whenever you know it. The same applies to dayfirst=True, which is a hint rather than a guarantee.
Try it yourself
  • Add flights['IsWeekend'] = when.dt.weekday >= 5 and count how many flights fall on a weekend.
  • Drop the format argument from pd.to_datetime and compare the first three dates with the ones above.
  • Build Date with .apply(lambda x: x.split('/')[0]) instead of .str.split and time both with %%timeit.

Slow is fine. Stopping is the only problem.