0Pricing
Pandas & NumPy Academy · レッスン

query()メソッド

query()で読みやすいフィルタ式を文字列として記述し、@を使ってクエリ内でPython変数を参照し、フィルタを連結します。

「query()メソッド」はCoddyKit上の無料Pandas & NumPy Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはPandas & NumPy Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Pandas & NumPy Academyコースには全4レッスンが含まれています。

query()を使う理由

Pandasのブールインデックスは強力ですが、多くの条件を組み合わせると冗長になることがあります。query()メソッドを使うと、フィルター式を通常の文字列として記述できるため、多くの人にとって読みやすくなります。特にSQLの経験がある人にはわかりやすい書き方です。df[(df['age'] > 30) & (df['salary'] > 50000)]と書く代わりに、df.query('age > 30 and salary > 50000')と記述できます。

クエリ文字列はDataFrameの列名を基準に評価されるため、文字列の中では列名が変数名のように扱われます。

import pandas as pd

df = pd.DataFrame({
    'name': ['Alice', 'Bob', 'Carol', 'Dave'],
    'age': [25, 35, 28, 45],
    'salary': [50000, 90000, 70000, 120000]
})

# Boolean indexing style
result1 = df[(df['age'] > 27) & (df['salary'] > 60000)]

# Equivalent query() style
result2 = df.query('age > 27 and salary > 60000')

print(result2)
#     name  age  salary
# 1    Bob   35   90000
# 2  Carol   28   70000

クエリ文字列内の構文規則

クエリ文字列内では、標準的なPythonの比較演算子(>、<、==、!=、>=、<=)と、論理演算子and、or、notを使用できます。通常のブールインデックスとは異なり、ここでは英単語を使えるため、アンパサンドやパイプは必要ありません。

空白を含む列名やPythonのキーワードと競合する列名は、バッククォートで囲む必要があります。df.query('`first name` == "Alice"')のように記述します。

import pandas as pd

df = pd.DataFrame({
    'product': ['A', 'B', 'C', 'D'],
    'price': [10, 200, 50, 300],
    'in_stock': [True, False, True, True]
})

# Use 'and', 'or', 'not' in query strings
result = df.query('price > 40 and in_stock == True')
print(result)
#   product  price  in_stock
# 2       C     50      True
# 3       D    300      True

@を使ったPython変数の参照

query()の重要な機能の1つは、@接頭辞を使って、周囲のスコープにあるPython変数を参照できることです。これにより、フィルターのパラメーター化が簡単になります。しきい値を計算して変数に格納し、値をハードコードする代わりに、クエリ文字列内で@variable_nameを使用します。

このパターンは、実行時にフィルター引数を受け取る関数で特に便利です。

import pandas as pd

df = pd.DataFrame({
    'city': ['NYC', 'LA', 'Chicago', 'Houston'],
    'population': [8336817, 3979576, 2693976, 2320268]
})

min_pop = 3_000_000  # Python variable

# Use @ to inject the variable into the query
result = df.query('population > @min_pop')
print(result)
#    city  population
# 0   NYC     8336817
# 1    LA     3979576

query()呼び出しの連結

query()は新しいDataFrameを返すため、複数のquery呼び出しを連結したり、他のPandasメソッドとパイプラインで組み合わせたりできます。連結すると、各フィルタリング処理をそれぞれ1行に記述できるため、ロジックの読み取り、デバッグ、変更が簡単になります。

.groupby()、.sort_values()、.head()などのメソッドをquery()と連結して、簡潔な分析パイプラインを構築することもできます。

import pandas as pd

df = pd.DataFrame({
    'region': ['North', 'South', 'North', 'East', 'South'],
    'year': [2022, 2022, 2023, 2023, 2023],
    'revenue': [100, 200, 150, 80, 300]
})

# Chain two query calls
result = (df
    .query('year == 2023')
    .query('revenue > 100')
    .sort_values('revenue', ascending=False)
)
print(result)
#    region  year  revenue
# 4   South  2023      300
# 2   North  2023      150

query()とブールインデックスの比較

どちらの方法でも同じ結果を得られますが、それぞれ適した場面があります。query()は、ブールインデックスでは多くの括弧が必要になる複数条件のフィルターに適しています。一方、query文字列ではサポートされないメソッド呼び出しのような、複雑なPython式を条件に含める場合は、ブールインデックスのほうが適しています。

ほとんどの場合、パフォーマンスは同程度です。ただし、query()は内部でnumexprライブラリを使用するため、非常に大きなDataFrameでは少し高速になることがあります。

import pandas as pd

df = pd.DataFrame({
    'A': range(10),
    'B': range(10, 20)
})

# Boolean indexing
result_bool = df[(df['A'] > 2) & (df['B'] < 18) & (df['A'] != 5)]

# Equivalent query — much more readable
result_query = df.query('A > 2 and B < 18 and A != 5')

print(result_query)
#    A   B
# 3  3  13
# 4  4  14
# 6  6  16
# 7  7  17

query()での文字列比較

クエリ文字列内で文字列リテラルを引用符で囲むと、文字列列の値を対象にフィルタリングできます。外側のクエリ文字列にダブルクォート、文字列値にシングルクォートを使うか、その逆にします。ただし、組み合わせは一貫させてください。query()はcontains()のような.strメソッドをサポートしていないため、その場合は.strを使ったブールインデックスを使用します。

import pandas as pd

df = pd.DataFrame({
    'country': ['USA', 'UK', 'Canada', 'USA', 'Germany'],
    'sales': [400, 150, 200, 600, 300]
})

# Single quotes for the string value inside double-quoted query
result = df.query('country == "USA"')
print(result)
#   country  sales
# 0     USA    400
# 3     USA    600

in演算子とnot in演算子の使用

クエリ文字列内では、Pythonのリストと同様に、メンバーシップテスト用のin演算子とnot in演算子を使用できます。これは、複数の==条件をorで連結するよりもすっきりした方法です。値のリストはクエリ文字列に直接記述します。

import pandas as pd

df = pd.DataFrame({
    'status': ['active', 'inactive', 'pending', 'active', 'closed'],
    'amount': [100, 200, 50, 300, 150]
})

# Keep rows where status is in a specific list
result = df.query('status in ["active", "pending"]')
print(result)
#     status  amount
# 0   active     100
# 2  pending      50
# 3   active     300

# Exclude certain statuses
excluded = df.query('status not in ["closed", "inactive"]')
print(excluded.shape)  # (3, 2)

query()による数値範囲フィルター

10 < price < 500のようなPythonの連鎖比較はクエリ文字列内で使用できるため、範囲フィルターを表現力豊かに記述できます。標準的なブールインデックスでは、between()を使うか、2つの条件を&で結合しなければ同じことはできません。

import pandas as pd

df = pd.DataFrame({
    'product': ['A', 'B', 'C', 'D', 'E'],
    'price': [5, 50, 150, 300, 500]
})

# Chained comparison — only works inside query()
result = df.query('50 <= price <= 300')
print(result)
#   product  price
# 1       B     50
# 2       C    150
# 3       D    300

query()の制限

query()は強力ですが、いくつか制限があります。空白や特殊文字を含む列名には、バッククォートによる引用が必要です。カスタム関数や複数行のロジックなど、非常に複雑なPython式は文字列内でサポートされません。また、列名がclassやifのようなPythonのキーワードと競合すると、query()が予期しない結果を返すことがあります。この場合もバッククォートで囲んでください。

query()で明確に表現できない処理には、標準的なブールインデックスを使用してください。

import pandas as pd

df = pd.DataFrame({'first name': ['Alice', 'Bob'], 'class': [1, 2]})

# Backtick-quote column names with spaces or reserved words
result = df.query('`first name` == "Alice" and `class` == 1')
print(result)
#   first name  class
# 0      Alice      1

実践例:注文のフィルタリング

ここでは、query()と変数参照を組み合わせ、さらにgroupbyへ連結する実践例を示します。まずフィルタリングしてから集計するこのパターンでは、不要な行を処理せずに済むため、コードが明確になるだけでなく、大規模なデータセットでは処理も高速になります。

import pandas as pd

orders = pd.DataFrame({
    'region': ['East', 'West', 'East', 'West', 'East'],
    'year': [2023, 2023, 2024, 2024, 2024],
    'revenue': [100, 200, 300, 400, 500]
})

min_year = 2024

# Filter to recent orders in the East region, then sum revenue
summary = (
    orders
    .query('year >= @min_year and region == "East"')
    .groupby('region')['revenue'].sum()
)
print(summary)
# region
# East    800
# Name: revenue, dtype: int64

query()とメソッド連結のベストプラクティス

メソッドチェーン内でquery()を使用することは、現代的なPandasコードにおけるベストプラクティスです。変換パイプラインの各ステップが明確になり、コード自体が処理内容を説明する形になります。チェーン全体を括弧で囲むと、バックスラッシュを使わずに複数行へ分割できます。

assign()で列を追加し、groupby()で集計し、pipe()でカスタム関数を適用するなど、query()と組み合わせて読みやすいパイプラインを構築できます。

import pandas as pd

df = pd.DataFrame({
    'dept': ['Eng', 'HR', 'Eng', 'Sales', 'HR'],
    'salary': [90000, 50000, 120000, 70000, 55000],
    'years': [3, 5, 8, 2, 4]
})

result = (
    df
    .query('years > 2 and dept != "HR"')
    .assign(bonus=lambda x: x['salary'] * 0.1)
    .sort_values('salary', ascending=False)
)
print(result[['dept', 'salary', 'bonus']])
#    dept  salary   bonus
# 2   Eng  120000  12000.0
# 0   Eng   90000   9000.0

理解度チェック

query()メソッドの理解度を確認しましょう。

レッスンのまとめ

このレッスンでは、query()はフィルター式を文字列として受け取るため、複数条件のフィルターを読みやすく記述できること、@variable_nameでPython変数をクエリに組み込めること、そして標準的なブールインデックスでは使えないin/not inや連鎖比較を使用できることを学びました。次は、メンバーシップや範囲のフィルターを簡潔に記述するisin()とbetween()について学びます。

よくある質問

「query()メソッド」レッスンは無料ですか?

はい。「query()メソッド」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Pandas & NumPy Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Pandas & NumPy Academyコースには全4レッスンが含まれています。

「query()メソッド」で何を学びますか?

query()で読みやすいフィルタ式を文字列として記述し、@を使ってクエリ内でPython変数を参照し、フィルタを連結します。 ブラウザで直接実行するハンズオンコードでPandas & NumPy Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Pandas & NumPy Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのPandas & NumPy Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。

「query()メソッド」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このPandas & NumPy Academyレッスンでコードを書いて実行できますか?

はい。すべてのPandas & NumPy Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. DataFrameのブールインデックス
  2. query()メソッド
  3. isin()とbetween()によるフィルタ
  4. パターンによる列の選択
← Pandas & NumPy Academyに戻る