pandasのilocは、DataFrameやSeriesから行と列を位置(0から数える整数の番号)で取り出すためのアクセサです。名前の由来はinteger location(整数位置)で、df.iloc[行番号, 列番号]と書きます。列名や行ラベルで取り出すlocと対になり、番号で指定したいならiloc、名前で指定したいならlocを使います。
この記事のコードは、すべてpandas 3.1.0(2026年10月22日リリース)とPython 3.13で実行して出力を確かめています。pandas 3.0からは、ilocにブール条件のSeriesを渡せるようになるなど、古い解説記事と結果が食い違う箇所があります。サンプルには次のDataFrameを使います。
import pandas as pd
df = pd.DataFrame({
'名前': ['佐藤', '鈴木', '田中', '高橋', '伊藤'],
'年齢': [28, 34, 45, 23, 39],
'部署': ['営業', '開発', '人事', '開発', '営業'],
'年収': [420, 560, 680, 380, 510],
})
# 行の位置は 0〜4、列の位置は 名前=0, 年齢=1, 部署=2, 年収=3
まとめ:ilocの書き方早見表
よく使う書き方を先に並べます。表の「行目・列目」は、通常の数え方とは異なり0始まりの位置を表します。DataFrameの戻り値は抽出件数だけでなく指定方法で決まります。行・列の一方を単一の整数で指定するとSeries、両方を単一の整数で指定するとスカラー値、両方の軸をリストやスライスで残すとDataFrameになります。
| やりたいこと | 書き方 | 戻り値 |
|---|---|---|
| 先頭行 | df.iloc[0] |
Series |
| 最終行 | df.iloc[-1] |
Series |
| 最終行を表のまま | df.iloc[[-1]] |
DataFrame |
| 1〜2行目 | df.iloc[1:3] |
DataFrame(3は含まない) |
| 0・2・4行目 | df.iloc[[0, 2, 4]] |
DataFrame |
| 1列目(年齢) | df.iloc[:, 1] |
Series |
| 最終列以外 | df.iloc[:, :-1] |
DataFrame |
| 1行2列目の値 | df.iloc[1, 2] |
スカラー(’開発’) |
押さえる点は3つに絞れます。スライスは終端を含まないこと、単一の番号で範囲外を指すとIndexErrorになること、並べ替えたあとはilocとlocが別の行を指すことです。pandas 3.0以降は条件式のSeriesもilocに渡せますが、行ラベルが文字列のDataFrameでは今もエラーになります。詳しくは後半の「pandas 3.0以降で変わった点」で扱います。
ilocとは:位置で行と列を取り出すアクセサ
pandas公式リファレンスのDataFrame.ilocは、ilocを「位置による選択のための、純粋に整数位置ベースのインデックス参照」と説明し、受け付ける入力を次の6種類に限っています。
- 整数(例:
5) - 整数のリストや配列(例:
[4, 3, 0]) - 整数のスライス(例:
1:7) - ブール配列
- 呼び出し元のDataFrameを受け取り、上のいずれかを返す関数
- 行と列の指定を組にしたタプル(例:
(0, 1))
列名の文字列は入力に含まれません。df.iloc[:, '年齢']と書くとValueErrorになります。整数・リスト・スライスやブール配列を渡す場合、ilocは行ラベルによらず位置で選択します。ただし、本記事で扱うpandas 3.0以降のブールSeriesには、後述するラベル照合の挙動があります。配列を番号で参照する考え方そのものはNumPyとは?読み方・インストール・基本的な使い方をPythonで解説(NumPy 2.5対応)で解説しています。
# 1行目(鈴木の行)をSeriesで取得
print(df.iloc[1])
# 名前 鈴木
# 年齢 34
# 部署 開発
# 年収 560
# Name: 1, dtype: object
# 1行目・2列目(部署)の値
print(df.iloc[1, 2]) # 開発
1行を取り出したSeriesのdtypeはobjectになります。文字列の列と整数の列が混ざった行を1本のSeriesにまとめるためです。取り出した年収で計算するなら、df.iloc[1, 3]のようにセル単位で取るか、列を先に絞ってから行を取ると整数のまま扱えます。
ilocとlocの違い:位置とラベル、スライス終端の扱い
ilocは位置、locはラベル(行名・列名)で指定します。作ったばかりのDataFrameは行ラベルが0, 1, 2…の連番なので、番号とラベルが一致して違いが見えません。差がはっきり出るのはスライスの終端と、並べ替えたあとです。
| 観点 | iloc | loc |
|---|---|---|
| 指定の基準 | 位置(0始まりの整数) | ラベル(行名・列名) |
| 列の指定 | 列番号 | 列名 |
| スライスの終端 | 含まない | 含む |
| 新しい行・列の追加 | 不可(IndexError) | 可 |
ilocとlocのスライス終端の扱い
同じ0:2でも、ilocは2行、locは3行を返します。ilocのスライスはPythonのリストと同じく「終端の手前まで」、locはラベルの範囲なので終端のラベルも含みます。
print(df.iloc[0:2]) # 0, 1 の2行
# 名前 年齢 部署 年収
# 0 佐藤 28 営業 420
# 1 鈴木 34 開発 560
print(df.loc[0:2]) # 0, 1, 2 の3行
# 名前 年齢 部署 年収
# 0 佐藤 28 営業 420
# 1 鈴木 34 開発 560
# 2 田中 45 人事 680
並べ替え後のiloc[0]とloc[0]の参照先
sort_values()で並べ替えると、行ラベルは元の番号のまま順番だけが入れ替わります。ここでiloc[0]は並べ替え後の先頭行、loc[0]はラベルが0の行(元の先頭行)を返します。
s = df.sort_values('年齢', ascending=False)
print(s.index.tolist()) # [2, 4, 1, 0, 3]
print(s.iloc[0]['名前']) # 田中(年齢が最も高い行)
print(s.loc[0]['名前']) # 佐藤(ラベル0の行)
print(s.iloc[0:2]['名前'].tolist()) # ['田中', '伊藤']
print(s.loc[0:2]['名前'].tolist()) # []
最後のs.loc[0:2]は空になります。並べ替え後の行ラベルは2, 4, 1, 0, 3の順で、ラベル0がラベル2より後ろにあるため、「0から2まで」の範囲に入る行がありません。エラーにならず空のDataFrameが返るので、気付かないまま後続の処理へ進みやすい箇所です。「上位n件」「最新の1件」のように順番で取る処理はilocで書くと決めておくと、この取り違えは起きません。
ilocで行を取り出す:先頭・最終行・範囲・飛び飛び
行だけを指定するときは、列の指定を省略できます。単一の番号ならSeries、リストかスライスならDataFrameが返ります。
df.iloc[0] # 先頭行(Series)
df.iloc[[0]] # 先頭行(DataFrame)
df.iloc[1:3] # 1, 2行目
df.iloc[[0, 2, 4]] # 飛び飛びの行
df.iloc[:3] # 先頭3行(head(3) と同じ)
df.iloc[-3:] # 末尾3行(tail(3) と同じ)
df.iloc[::2] # 1行おき(0, 2, 4行目)
番号を二重の角かっこで囲むかどうかで型が変わります。1行だけでも、あとでto_csv()やmerge()に渡すならDataFrameのほうが扱いやすいので、df.iloc[[0]]と書きます。
iloc[-1]による最終行の取得と空データへの対処
負の番号は末尾から数えます。df.iloc[-1]は空でないDataFrameの最後の行を返します。時系列データの最新レコードとして扱うには、日時の昇順に並んでいることが前提です。
print(df.iloc[-1])
# 名前 伊藤
# 年齢 39
# 部署 営業
# 年収 510
# Name: 4, dtype: object
print(df.iloc[-1, 3]) # 最終行の年収 510
注意するのは空のDataFrameです。フィルタの結果が0行のとき、iloc[-1]はIndexError: single positional indexer is out-of-boundsで止まります。0行になりうる処理では、iloc[-1:](空なら空のDataFrameを返す)にするか、if not result.empty:で先に確かめます。
empty = df[df['年齢'] > 100]
# empty.iloc[-1] → IndexError
print(empty.iloc[-1:]) # Empty DataFrame(エラーにならない)
ilocで列を取り出す:番号指定と列名からの変換
列を取り出すときは、行の位置にコロン:(全行)を置き、2つめに列番号を書きます。
df.iloc[:, 1] # 年齢列(Series)
df.iloc[:, [0, 3]] # 名前と年収
df.iloc[:, 1:3] # 年齢と部署(3は含まない)
df.iloc[:, -1] # 最終列(年収)
df.iloc[0:2, 0:2] # 先頭2行 × 先頭2列
列番号での指定は、列の追加や並べ替えで番号がずれると黙って別の列を返します。外部から受け取るCSVのように列構成が変わりうるデータでは、列名で書けるlocのほうが安全です。
「行は位置、列は名前」で指定したい場面もあります。ilocとlocは1回の呼び出しで混ぜられないため、列名を番号に変換してからilocに渡します。get_loc()は1列、get_indexer()は複数列を変換します。
# 先頭3行の「年収」列
df.iloc[:3, df.columns.get_loc('年収')]
# 全行の「名前」と「年収」列
positions = df.columns.get_indexer(['名前', '年収'])
if (positions < 0).any():
raise KeyError('指定した列が存在しません')
df.iloc[:, positions]
行と列を同時に指定する:交差抽出とiatの使い分け
行と列の両方を渡すと、その交差部分が返ります。整数・リスト・スライスは行と列で自由に組み合わせられます。
df.iloc[1, 2] # '開発'(スカラー)
df.iloc[[0, 2], [1, 3]] # 0, 2行目 × 年齢, 年収
df.iloc[0:3, [0, 3]] # 0〜2行目 × 名前, 年収
1つのセルだけを読み書きするならiatが向いています。公式リファレンスのDataFrame.iatは、iatを「整数位置で行と列の組の単一の値にアクセスする」ものと定義しています。リストやスライスを解釈する処理を通らないぶん、1回あたりの負荷が小さくなります。
1,000行×5列の数値のDataFrameで、timeit.timeitで同じセルへのアクセスを20万回実行し、総所要時間を20万で割った1回当たりの時間は、iloc[500, 3]が12.27マイクロ秒、iat[500, 3]が4.77マイクロ秒で、iatが約2.6倍速い結果でした(Intel Mac、Python 3.13.14、pandas 3.1.0、NumPy 2.5.4)。ただし、どちらを使っても1セルずつ読むループは遅いままです。数万行を処理するなら、df['年収'] * 1.1のように列単位の演算に書き換えるほうが効きます。
pandas 3.0以降で変わった点:ブール条件・整数キー・代入
pandas 3.0.0(2026年1月21日リリース)で、ilocの周辺にいくつか挙動の変更が入りました。2.x系の時代に書かれた解説や、手元の古いコードと結果が合わないときは、まずこの章の項目を疑ってください。変更の一覧はpandas 3.0.0のリリースノートにあります。
pandas 3.0以降のブールSeries対応とインデックスの制約
リリースノートのIndexingの項に「DataFrame.ilocとSeries.ilocが__getitem__でブールマスクに対応した」とあります(issue 60994)。2.x系ではdf.iloc[df['年齢'] > 30]がNotImplementedError: iLocation based boolean indexing on an integer type is not availableで止まっていましたが、3.0.0と3.1.0では通ります。
# pandas 3.0 以降:行ラベルが整数なら通る
print(df.iloc[df['年齢'] > 30].index.tolist()) # [1, 2, 4]
# 行ラベルが文字列だと、3.1.0 でもエラー
named = df.set_index('名前')
# named.iloc[named['年齢'] > 30]
# → ValueError: iLocation based boolean indexing cannot use an indexable as a mask
# 版や行ラベルに関係なく通る書き方
named.iloc[(named['年齢'] > 30).to_numpy()]
named[named['年齢'] > 30]
3.1.0のソースコード(pandas/core/indexing.pyの_validate_key)を確かめると、ブールSeriesを受け入れるのは、そのSeriesの行ラベルが整数のときだけです。受け入れた場合も位置ではなく行ラベルで突き合わせて行を選ぶため、並び順が違うマスクを渡しても同じ行が返ります。行ラベルが文字列や日付のDataFrameでは従来どおりエラーなので、条件での抽出はilocでなくdf[条件]かdf.loc[条件]で書くのが、どの版でも動く選び方です。ilocを使い続けたいなら.to_numpy()でラベルを外したブール配列を渡します。
pandas 3.0以降のSeries[整数]のラベル参照
3.0.0では「Series.__getitem__とSeries.__setitem__は整数のキーを常にラベルとして扱い、位置としては扱わない」と変更されました(issue 50617)。2.x系では、行ラベルが文字列のSeriesにs[0]と書くと警告付きで先頭要素が返りましたが、3.0以降はKeyErrorになります。
ser = pd.Series([10, 20, 30], index=['a', 'b', 'c'])
# ser[0] → KeyError: 0
print(ser.iloc[0]) # 10
ser2 = pd.Series([10, 20, 30], index=[2, 1, 0])
print(ser2[0]) # 30(ラベル0の値)
print(ser2.iloc[0]) # 10(先頭の値)
「先頭の値」が欲しいときは、Seriesでも必ずiloc[0]と書きます。行ラベルが整数のSeriesで[0]を使うと、エラーにならないまま別の値を返すので見落としやすくなります。
型の合わない値を代入するとTypeError
ilocは代入にも使えます。ただし3.0.0で、列の型に収まらない値を代入したときに列の型を自動で広げる動作が禁止されました(PDEP-6)。たとえば2.3.3では、整数列への文字列代入は警告付きでobject型へ、小数28.5の代入は警告付きでfloat64型へ変換されていましたが、これらの代入は3.0以降はTypeErrorで止まります。
df2 = df.copy()
df2.iloc[1, 3] = 600 # 整数の列に整数:OK
# df2.iloc[0, 1] = 28.5 → TypeError: Invalid value '28.5' for dtype 'int64'
# df2.iloc[0, 1] = 'abc' → TypeError: Invalid value 'abc' for dtype 'int64'
# 型を変えたいなら、先に列の型を変換する
df2['年齢'] = df2['年齢'].astype('float64')
df2.iloc[0, 1] = 28.5
また、ilocで存在しない位置に代入しても行は増えません。df2.iloc[5, 0] = '山本'はIndexError: iloc cannot enlarge its target objectになります。行を足すならpd.concat()か、新しいラベルを指定したdf.loc[5] = [...]を使います。
Copy-on-Writeによる連鎖代入の制約
df[df['年収'] > 500].iloc[0, 0] = '更新'のように、絞り込みとilocの代入を続けて書く「連鎖代入」は、pandas 3.0でCopy-on-Writeが既定になったことで元のDataFrameに一切反映されなくなりました。3.1.0ではChainedAssignmentErrorという警告が出るだけで、エラーでは止まりません。
df3 = df.copy()
df3[df3['年収'] > 500].iloc[0, 0] = '更新' # 警告のみ・df3 は変わらない
print(df3.iat[1, 0]) # 鈴木
# 1回の loc で行と列を同時に指定する
df3.loc[df3['年収'] > 500, '名前'] = '更新'
print(df3['名前'].tolist()) # ['佐藤', '更新', '更新', '高橋', '更新']
ilocで取り出した一部分(sub = df.iloc[0:2])を書き換えても、元のdfは変わりません。元を書き換えたいときは、元のDataFrameに対して1回のilocかlocで代入します。Copy-on-Writeの仕組みはCopy-on-Write(コピーオンライト)とは?仕組みとfork・ファイルシステム・Pandas 3.0での使われ方で解説しています。3.1.0のそのほかの変更点はPandasとは?できること・使い方とPandas 3.1の変更点【2026年10月】にまとめています。
ilocのエラーメッセージと対処法
ilocで出るエラーは、ほとんどが「番号のルール」に反したときのものです。pandas 3.1.0で実際に出たメッセージと原因を並べます。
| メッセージ | 例 | 原因 |
|---|---|---|
| IndexError: single positional indexer is out-of-bounds | df.iloc[10] |
単一の番号が範囲外 |
| IndexError: positional indexers are out-of-bounds | df.iloc[[0, 10]] |
リスト内に範囲外の番号 |
| IndexError: Boolean index has wrong length | df.iloc[[True, False]] |
ブール配列の長さ違い |
| IndexError: iloc cannot enlarge its target object | df.iloc[5, 0] = 'x' |
範囲外への代入 |
| ValueError: Location based indexing can only have … | df.iloc[:, '年齢'] |
列名の文字列を渡した |
| TypeError: Cannot index by location index with a non-integer key | df.iloc[1.0] |
小数を渡した |
範囲外の番号は、公式リファレンスに「要求された位置が範囲外ならIndexErrorを出す。ただしスライスは範囲外を許す(Pythonやnumpyのスライスの意味に合わせている)」とあるとおり、スライスで書くとエラーになりません。df.iloc[3:100]は3行目と4行目の2行を返します。逆に言えば、スライスで行数を取り違えてもエラーで気付けないので、件数が決まっている処理ではlen()で確かめます。
小数の番号は、len(df) / 2のように割り算の結果をそのまま渡したときに起きます。len(df) // 2(切り捨ての割り算)かint()で整数にしてから渡してください。
ilocを使う場面と避ける場面
ilocは「順番そのものに意味があるデータ」で強く、「列の意味が名前で決まるデータ」では弱いアクセサです。判断の基準は次のとおりです。
ilocで書くべき場面は、並べ替えた結果の上位n件、時系列の直近n件、学習用とテスト用の位置での分割、最終列を目的変数にする特徴量の切り出しです。いずれも「何番目か」が処理の意味なので、行ラベルが飛んでいても正しく動きます。
# 時系列:古い順に並んだデータの先頭8割を学習、残りを評価に使う
n_train = int(len(df) * 0.8)
train = df.iloc[:n_train]
test = df.iloc[n_train:]
# 最終列を目的変数、それ以外を説明変数にする
X = df.iloc[:, :-1]
y = df.iloc[:, -1]
時系列データをランダムに分割すると未来の値で過去を予測する形になるため、位置での分割が基本です。欠損補間やリサンプリングを含む前処理全体は時系列データの前処理|欠損補間・リサンプリング・特徴量生成とリーク回避を実装手順で解説を参照してください。
ilocを避けるべき場面は、外部から受け取るCSVやAPIの結果のように列の数や並びが変わりうるデータです。末尾に別の列が追加された状態でy = df.iloc[:, -1]を使うと、その追加列を目的変数として扱い、X = df.iloc[:, :-1]には本来の目的変数が混入します。どちらもエラーは出ません。この場合はdf.drop(columns='年収')とdf['年収']のように列名で書きます。条件での抽出も、前の章のとおりlocかdf[条件]で書きます。
処理が遅い場合は、まず切り出し・集計・読み込みの所要時間とメモリ使用量を分けて測定します。処理方法を改善しても要件を満たさない場合に、別ライブラリへの移行を検討します。pandasとの違いはPolarsとは?pandasとの違い・高速化の仕組みと実務での採用判断【実装目線】で比較しています。pandasで組んだ集計処理を業務システムに組み込む場合は、フルスクラッチ開発でのご相談も承っています。
よくある質問
ilocとlocはどちらを使えばよいですか?
「上からn番目」のように位置で取りたいならiloc、列名や行ラベルで取りたいならlocです。並べ替えたあとの先頭行をSeriesで取るならiloc[0]が直接的です。DataFrameのままならhead(1)でも取得できます。条件での抽出や列名での指定にはlocを使います。スライスの終端はilocが含まず、locが含みます。
iloc[0]とiloc[-1]は何を返しますか?
df.iloc[0]は先頭行、df.iloc[-1]は最終行を、それぞれSeriesで返します。表の形のまま欲しいときはdf.iloc[[0]]、df.iloc[[-1]]と二重の角かっこにします。DataFrameが空だとどちらもIndexErrorになるので、0行になりうる場合はiloc[:1]やiloc[-1:]を使います。
df.iloc[条件式]は使えますか?
条件式が同じDataFrameから作ったブールSeriesを返す場合、pandas 3.0以降では行ラベルが整数なら使えます。ブール配列の場合は、古い版や文字列・日付の行ラベルでも使えます。ブールSeriesは、2.x系と、行ラベルが文字列や日付のDataFrameではエラーになります。どの版でも動かすならdf[条件]かdf.loc[条件]、ilocにこだわるならdf.iloc[(条件).to_numpy()]と書きます。
ilocとiatの違いは何ですか?
どちらも整数位置で指定しますが、iatは1つのセル専用、ilocはセル・行・列・範囲のすべてに使えます。本記事の1,000行×5列の数値DataFrameを使った計測では、iatの1セル読み出しはilocの約2.6倍でした。速度差はデータ型や実行環境によって変わります。範囲やリストを渡す処理はilocで書きます。
ilocは何の略ですか?
integer location(整数位置)の略で、公式リファレンスも「integer-location based indexing」と説明しています。1セル用のアクセサも同じ対になっていて、iatは整数位置、atはラベルで1つの値を指します。先頭にiが付くものは番号、付かないものはラベルで指定すると覚えると区別できます。