A Python Beginner’s Look at .loc. As a Python beginner, using .loc to... | by Joseph H | Towards Data Science

As a Python beginner, using .loc to retrieve and update values in a pandas dataframe just wasn’t clicking for me. The SettingWithCopyWarning message Python kept throwing at me made it clear that I needed to use it, but it felt like a lot of trial-and-error-messages to get it to do what I needed.
I sat down played around enough with it that I’m now starting to get comfortable, so I figured I’d share my walkthrough; if you’re a beginner, hopefully it will help you. In this post, I cover how to get data with .loc; I’ll cover how to set data in a future post.
I did my “poking” around using a Pokémon dataset (#dadjokes) from the first week of my Data Science Immersive course at General Assembly in Atlanta.
Here’s what df.head(10) looks like:
My first tip is that .loc is not a dataframe method; using () instead of [] cost me 45 minutes one Saturday. Inside those square brackets, .loc takes two inputs: a row “indexer” and an (optional, separated by a comma) column “indexer”: no other arguments, like inplace or axis.
So what can you use as indexers? The pandas documentation lays out the allowed inputs, and the first one it discusses is a single label, so let’s dive in.
Passing a single indexer (since this dataframe is RangeIndexed, we pass an integer) returns a pandas series with the data for the row with that index value, in this case Charmander:
df.loc[4]Name              CharmanderType                    FireTotal                    309HP                        39Attack                    52Defense                   43SpecialAttack             60SpecialDefense            50Speed                     65Name: 4, dtype: object
.loc will always interpret integers as labels, not as integer positions along the index (you can use .iloc for that).
Passing just a column label or a blank row indexer will give you an error, because the first position of the bracketed index is looking for the row index, and it’s required:
>>>df.loc['Type']KeyError: 'Type'>>>df.loc[, 'Type']SyntaxError: invalid syntax
But passing the column label after using Python slice notation to specify what rows you want (e.g. [:]for all rows) will give you the column data in a pandas Series. Note that, unlike the usual Python convention, .loc slices include both endpoints:
df.loc[:5,'Type']0    GrassPoison1    GrassPoison2    GrassPoison3    GrassPoison4           Fire5           FireName: Type, dtype: object
Slice-ability on row and column index names is a nice advantage of .loc, since normal bracket notation on a dataframe doesn’t allow it:
df.loc[:5,'Type':'Speed']
You can also pass lists with row or column index names to get a subset of the data. (List comprehensions work, too!):
df.loc[[2,4,10,99],['Name','HP']]
The last type of value you can pass as an indexer is a Boolean array, or a list of True and False values. This method has some real power, and great application later when we start using .loc to set values. Rows and columns that correspond to False values in the indexer will be filtered out. The array doesn’t have to be the same size as the corresponding index, but .loc will treat the missing values as if they were False:
df.loc[       [True, False, False, True, True],       [True, False, True, False, False, True]]
You can also use Boolean masks to generate the Boolean arrays you pass to .loc. If we want to see just the “Fire” type Pokémon, we’d first generate a Boolean mask — df[‘Type’] == ‘Fire’ — which returns a series of True/False values for each row in the dataframe. We then pass that mask as the row indexer in .loc:
df.loc[df['Type'] == 'Fire']
All sorts of possibilities here, like getting Pokémon with more than 175 attack:
df.loc[df['Attack'] > 175]
Or those with less than 100 hit points and greater than 650 total stats:
df.loc[(df['HP'] < 100) & (df['Total'] > 650)]
Or those with either the letter “X” in their names or of the “PsychicFairy” type:
df.loc[    (df['Name'].str.contains('X')) |         (df['Type'].str.contains('PsychicFairy'))]
Of course, you can do much of this with normal bracket notation on a DataFrame, so what’s so special? In this post, I talk about how .loc allows you to set values in the DataFrame.