The first look at a dataset
The first look at a dataset is four calls in a fixed order, shape, head, info and describe, which answer how big the table is, what a row looks like, what type each column holds and how the numbers are spread.
Last updated: 12 Oct, 2026 · pandas 3.0 · scikit-learn 1.9
Reading a CSV that is not UTF-8 got the Zomato table into a frame called df, 9551 rows and 21 columns of restaurant data. Nothing has been looked at yet. These four calls are what to run before any chart and before any decision, and they are taught once here for the whole course.
Syntax: one attribute and three methods, all on the frame.
df.shape # (rows, columns), an attribute, no brackets
df.head(n) # the first n rows, 5 by default
df.info() # one line per column: name, non-null count, dtype
df.describe() # count, mean, std, min, quartiles and max, numeric columns onlyCounting the rows and reading the first ones
shape is a tuple of rows and columns. columns lists the names, and head prints the first rows so that you can see what a value in each column looks like. to_string() keeps pandas from cutting the output short.
print("rows and columns:", df.shape)
print("columns:", df.columns.tolist()[:6], "...")
print(df[['Restaurant Name', 'City', 'Cuisines', 'Aggregate rating']].head(3).to_string())rows and columns: (9551, 21)
columns: ['Restaurant ID', 'Restaurant Name', 'Country Code', 'City', 'Address', 'Locality'] ...
Restaurant Name City Cuisines Aggregate rating
0 Le Petit Souffle Makati City French, Japanese, Desserts 4.8
1 Izakaya Kikufuji Makati City Japanese 4.5
2 Heat - Edsa Shangri-La Mandaluyong City Seafood, Asian, Filipino, Indian 4.4- 9551 rows and 21 columns. Small enough to read into memory and work with directly.
- Column names with spaces in them.
'Restaurant Name'can be reached withdf['Restaurant Name']and not withdf.Restaurant Name. Square brackets work for every name, so prefer them. Cuisinesholds several values in one cell, separated by commas. A column that holds a list is a column that will need splitting before a model sees it.
Reading the dtype audit
info() prints one line per column with its name, how many values are not missing, and its dtype. It is the densest of the four calls: in one screen it gives the type audit and the missing value audit together.
df.info()<class 'pandas.DataFrame'> RangeIndex: 9551 entries, 0 to 9550 Data columns (total 21 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 Restaurant ID 9551 non-null int64 1 Restaurant Name 9551 non-null str 2 Country Code 9551 non-null int64 3 City 9551 non-null str 4 Address 9551 non-null str 5 Locality 9551 non-null str 6 Locality Verbose 9551 non-null str 7 Longitude 9551 non-null float64 8 Latitude 9551 non-null float64 9 Cuisines 9542 non-null str 10 Average Cost for two 9551 non-null int64 11 Currency 9551 non-null str 12 Has Table booking 9551 non-null str 13 Has Online delivery 9551 non-null str 14 Is delivering now 9551 non-null str 15 Switch to order menu 9551 non-null str 16 Price range 9551 non-null int64 17 Aggregate rating 9551 non-null float64 18 Rating color 9551 non-null str 19 Rating text 9551 non-null str 20 Votes 9551 non-null int64 dtypes: float64(3), int64(5), str(13) memory usage: 1.5 MB
RangeIndex: 9551 entries, 0 to 9550is the row index. Every row has a label, and here the labels are the numbers 0 to 9550.Cuisinesshows 9542 non-null while every other column shows 9551. That difference of 9 is the only missing data in the table.int64andfloat64are whole numbers and decimals.Price rangeis stored asint64although it is a rating from 1 to 4, which is a category wearing a number's clothes.stris text. On pandas 2 these thirteen columns would sayobjectinstead.memory usage: 1.5 MBis what the frame costs in RAM, worth a glance before loading a file a hundred times this size.
Reading the numeric summary
dtypes.value_counts() turns the type audit into a count per type, which is the first question in the EDA checklist. describe() then summarises the numeric columns: how many values, their mean and standard deviation, the smallest and largest, and the three quartiles. Three columns are shown here to keep the table readable.
print(df.dtypes.value_counts().to_string())
print()
print(df[['Average Cost for two', 'Aggregate rating', 'Votes']].describe().round(2).to_string())str 13
int64 5
float64 3
Average Cost for two Aggregate rating Votes
count 9551.00 9551.00 9551.00
mean 1199.21 2.67 156.91
std 16121.18 1.52 430.17
min 0.00 0.00 0.00
25% 250.00 2.50 5.00
50% 400.00 3.20 31.00
75% 700.00 3.70 131.00
max 800000.00 4.90 10934.00- 13 text columns and 8 numeric ones. That split decides everything downstream: the text columns need encoding, the numeric ones need scaling.
Average Cost for twohas a mean of 1199 and a maximum of 800000. The 75th percentile is 700, so three quarters of the restaurants are under 700 and the largest value is more than a thousand times that. The mean sits far above the median of 400, which is what a long right tail does.Aggregate ratinghas a minimum of 0 on a scale that starts at 1. A zero is not a bad rating here, it is a restaurant with no rating yet.describe()skipped the thirteen text columns. It summarises numbers only. Passinclude='str'to get counts and most frequent values for the rest.
What each of the four calls answers
| Call | The question it answers | What to look for |
|---|---|---|
df.shape | How big is this | Rows you can hold in memory, columns you can read through |
df.head() | What does a row look like | Cells holding several values, odd placeholders, an index that is a column in disguise |
df.info() | What type is each column, and what is missing | A number stored as text, a category stored as a number, a non-null count below the row count |
df.describe() | How are the numbers spread | A mean far from the median, a minimum of 0 where 0 is impossible, a maximum far above the 75th percentile |
Where the first look pays off
- Before writing any other line. Four calls take ten seconds and decide which techniques the rest of the work needs.
- After every merge or concat. The shape is how you check that a join added the columns you expected and no rows you did not.
- When a number looks wrong. A mean that sits far from the median, or a maximum far above the 75th percentile, is the first sign of an outlier.
describe() silently covers only the numeric columns, so a table with eight numeric columns out of twenty-one gives a summary of eight. Check the count of columns in the summary against df.shape[1] before concluding that the data looks fine.Related
- Previous: Reading a CSV that is not UTF-8
- Next: The missing value map
- See also: Text columns and the str dtype for why
info()printsstrand notobject - Reference: DataFrame.describe in the pandas documentation
- Run
df.describe(include='str')and read the four rows it gives for the text columns: count, unique, top and freq. - Call
df.tail(3)and compare it withhead(3). Some files are sorted, and the last rows then look nothing like the first. - Run
df.info(memory_usage='deep')and compare the number with the 1.5 MB the plain call reports.
You understood something today that you didn't yesterday.