The label of the smallest value — and two questions that only it can answer.
Where is the smallest one? .min() will not tell you; it hands back a number and discards the row it came from. .idxmin() returns the index label instead, which is what .loc[] wants, so the pair gives you the losing row whole:
df.loc[df['column'].idxmin()]
So far this is just idxmax in a mirror, and the max, min, idxmax and idxmin page covers that shared half. What earns idxmin its own page is that two very common questions are minimum problems in disguise. "Which row is closest to this value?" is the minimum of an absolute difference. "When did this first happen?" is the minimum of an index that runs in time order.
Official documentation: Series.idxmin and DataFrame.idxmin.
The arguments that earn their keep
df.idxmin(axis='index', # down each column (default), or across each row
skipna=True, # ignore NaN, or refuse to answer
numeric_only=False) # every column, or only the numeric ones
The same three that min takes. axis='columns' is the one worth remembering: it reads across each row and returns a column name, so on a wide table it answers "which of these measurements is this row's weakest."
A worked example, on real data
Our World in Data's figures for renewables as a share of electricity generation, one row per country per year. Their CDN turns away Pandas' default user agent, so pass one it recognizes:
import pandas as pd
url = 'https://ourworldindata.org/grapher/share-electricity-renewables.csv'
elec = (
pd.read_csv(url, storage_options={'User-Agent': 'Mozilla/5.0'})
.loc[lambda df_: df_['Code'].notna()]
)
latest = (
elec
.loc[lambda df_: (df_['Year'] == 2025) & (df_['Entity'] != 'World')]
.set_index('Entity')['Renewables']
)
latest.idxmin()
'Bangladesh'
A country name, because that is what the index holds. Now for the question idxmin is uniquely good at: which country sits closest to the world figure? That is not a minimum of the data at all — it is a minimum of the distance from a target:
world = elec.loc[lambda df_: (df_['Entity'] == 'World') & (df_['Year'] == 2025),
'Renewables'].iloc[0]
(latest - world).abs().nsmallest(3)
Entity
Argentina 0.820024
Bulgaria 0.962772
Hungary 1.552664
Name: Renewables, dtype: float64
.nsmallest(3) is there so you can see the field; drop it for (latest - world).abs().idxmin() and you get 'Argentina' on its own. Subtract, take the absolute value, take the label of the minimum — that is the whole nearest-match idiom, and it works the same against a date, a threshold, or another column.
The second question needs an index that runs in time order. Set the year as the index, group by country, and idxmin reports the year each country's renewable share bottomed out:
big = elec.loc[lambda df_: df_['Entity'].isin(
['Brazil', 'China', 'Germany', 'India', 'Japan', 'United States'])]
big.set_index('Year').groupby('Entity')['Renewables'].idxmin()
Entity
Brazil 2014
China 2003
Germany 1991
India 2003
Japan 1994
United States 2001
Name: Renewables, dtype: int64
Leave the index alone and the same groupby returns row labels instead, which .loc[] turns straight back into the rows themselves:
big.loc[big.groupby('Entity')['Renewables'].idxmin()]
Entity Code Year Renewables
984 Brazil BRA 2014 73.279144
1407 China CHN 2003 15.036272
2625 Germany DEU 1991 3.167792
3159 India IND 2003 11.695108
3440 Japan JPN 1994 7.919253
7118 United States USA 2001 7.513205
Germany's worst year was 3.2 percent, and Brazil's worst was 73 percent. That is the payoff for keeping the rows: groupby().min() would have given me six numbers with no way back to the countries and years behind them.
Three mistakes people make
Ties go to the first row, which is not the same as "the" answer. Gibraltar generated no renewable electricity at all for its entire 25 years in this data set, so its minimum is shared 25 ways, and .idxmin() reports 2000 without comment. On a sorted date index that tie-break is usually a gift — first occurrence is what you wanted. On an unsorted frame it is arbitrary. Compare against .min() with a boolean mask when it matters.
An empty selection raises rather than returning NaN. Filter down to nothing and .min() still answers nan, but .idxmin() has no label to give and raises ValueError: attempt to get argmin of an empty sequence. The same asymmetry hits an all-NaN column, where the message is ValueError: Encountered all NA values, and it is much worse inside a groupby, where one bad group aborts every group.
A label is not a position. The two groupby calls above returned different things — years, then labels like 2625 — because I changed the index between them, not because idxmin changed its mind. Those second ones look like row numbers and are not: big holds 236 rows, so big.iloc[2625] raises IndexError. Keep .idxmin() with .loc[], and use .argmin() when you genuinely want a position.
Where it shows up in Bamboo Weekly
#71: Holidays is the "when did it first happen" case, at scale. With the date column as the index, .groupby(['country', 'holiday']).idxmin() gives the first date each country-holiday pair was ever observed — one call, no sorting, no .head(1) per group.
#50: Red Sea shipping runs .idxmin() down a data frame of daily Suez Canal traffic and lands on March 2021, when the Ever Given wedged itself across the channel. Later in the same post, .agg(['idxmin', 'idxmax']) reports the best and worst quarters side by side.
#19: Working women sets out the boolean-mask version, df.loc[df['value'] == df['value'].min()], and then writes df.loc[df.idxmin()] instead. Chained after pct_change over the January readings, it finds the sharpest January-to-January fall in women's workforce participation: January 2021.
Practice it
Work through an idxmin exercise, with instant feedback and no signup required: practice.lernerpython.com/bamboo-weekly/idxmin/
Go deeper
idxmax is the same method upward, and has the boolean first-True trick that goes with it; max, min, idxmax and idxmin sets out the family. When you want the bottom few rather than the single lowest, nsmallest beats sort_values, and groupby is where idxmin earns most of its keep.
More Pandas videos on Python and Pandas with Reuven Lerner.
Bamboo Weekly is the practice. If you want the structured version — full courses with downloadable Jupyter notebooks, plus live Pandas office hours when you get stuck — that is LernerPython+Data. A paid Bamboo Weekly subscription is included with it.
Related methods
.min()— when you want the smallest value itself rather than where it sits.idxmax()— for the other end of the range.abs()— when the distance matters more than its direction
See it on real data
Below are the 7 Bamboo Weekly exercises that use idxmin on real-world data — try each one, then study the worked solution.
- Bamboo Weekly #177: European Summer
- Bamboo Weekly #119: Python conferences
- Bamboo Weekly #71: Holidays
- Bamboo Weekly #50: Red Sea shipping
- Bamboo Weekly #29: Auto accidents
- Bamboo Weekly #19: Working women
- Bamboo Weekly #10: Oil prices
Part of the Pandas Methods Index. See also practice by skill.