วิธีการเลือกแถวใน DataFrame ระหว่างสองค่าใน Python Pandas


105

ฉันกำลังพยายามแก้ไข DataFrame dfให้มีเฉพาะแถวที่ค่าในคอลัมน์closing_priceอยู่ระหว่าง 99 ถึง 101 และพยายามดำเนินการกับโค้ดด้านล่าง

อย่างไรก็ตามฉันได้รับข้อผิดพลาด

ValueError: ค่าความจริงของซีรี่ส์มีความคลุมเครือ ใช้ a.empty, a.bool (), a.item (), a.any () หรือ a.all ()

และฉันสงสัยว่ามีวิธีทำโดยไม่ใช้ลูปหรือไม่

df = df[(99 <= df['closing_price'] <= 101)]

ปัญหาที่นี่คือคุณไม่สามารถเปรียบเทียบสเกลาร์กับอาร์เรย์ได้ดังนั้นจึงเกิดข้อผิดพลาดสำหรับการเปรียบเทียบคุณต้องใช้ตัวดำเนินการแบบบิตและใส่ไว้ในวงเล็บเนื่องจากมีลำดับความสำคัญของตัวดำเนินการ
EdChum

df.queryและpd.evalดูเหมือนจะเหมาะกับกรณีการใช้งานนี้ สำหรับข้อมูลเกี่ยวกับpd.eval()ครอบครัวของฟังก์ชั่นคุณสมบัติของพวกเขาและกรณีการใช้งานกรุณาเยี่ยมแบบไดนามิกการแสดงออกในการประเมินผลโดยใช้หมีแพนด้า pd.eval ()
cs95

คำตอบ:


112

คุณควรใช้()เพื่อจัดกลุ่มเวกเตอร์บูลีนของคุณเพื่อลบความคลุมเครือ

df = df[(df['closing_price'] >= 99) & (df['closing_price'] <= 101)]

185

พิจารณาชุดระหว่าง :

df = df[df['closing_price'].between(99, 101)]

แพนด้ามีฟังก์ชัน "ไม่อยู่ระหว่าง" หรือไม่ ฉันไม่พบมัน
dsugasa

3
@dsugasa ใช้ตัวดำเนินการ tildeกับbetween.
Parfait

2
@dsugasa เช่นdf = df[~df['closing_price'].between(99, 101)]
ม.ค. 33

24

มีทางเลือกที่ดีกว่า - ใช้วิธีการquery () :

In [58]: df = pd.DataFrame({'closing_price': np.random.randint(95, 105, 10)})

In [59]: df
Out[59]:
   closing_price
0            104
1             99
2             98
3             95
4            103
5            101
6            101
7             99
8             95
9             96

In [60]: df.query('99 <= closing_price <= 101')
Out[60]:
   closing_price
1             99
5            101
6            101
7             99

UPDATE:ตอบความคิดเห็น:

ฉันชอบไวยากรณ์ที่นี่ แต่ล้มลงเมื่อพยายามรวมกับ expresison; df.query('(mean + 2 *sd) <= closing_price <=(mean + 2 *sd)')

In [161]: qry = "(closing_price.mean() - 2*closing_price.std())" +\
     ...:       " <= closing_price <= " + \
     ...:       "(closing_price.mean() + 2*closing_price.std())"
     ...:

In [162]: df.query(qry)
Out[162]:
   closing_price
0             97
1            101
2             97
3             95
4            100
5             99
6            100
7            101
8             99
9             95

ฉันชอบไวยากรณ์ที่นี่ แต่ล้มลงเมื่อพยายามรวมกับ expresison; df.query ('(mean + 2 * sd) <= closed_price <= (mean + 2 * sd)')
mapping dom

1
@mappingdom คืออะไรmeanและsd? ชื่อคอลัมน์เหล่านั้นหรือเปล่า
MaxU

ไม่ได้เป็นค่าเฉลี่ยที่คำนวณได้และค่าเบี่ยงเบนมาตรฐานที่จัดเก็บเป็นทศนิยม
การทำแผนที่โดม

@mappingdom คุณพูดว่า "เก็บไว้" หมายความว่าอย่างไร
MaxU

@mappingdom ฉันอัปเดตโพสต์ของฉันแล้ว - นั่นคือสิ่งที่คุณขอใช่หรือไม่
MaxU

10

คุณยังสามารถใช้.between()วิธีการ

emp = pd.read_csv("C:\\py\\programs\\pandas_2\\pandas\\employees.csv")

emp[emp["Salary"].between(60000, 61000)]

เอาต์พุต

ใส่คำอธิบายภาพที่นี่


7
newdf = df.query('closing_price.mean() <= closing_price <= closing_price.std()')

หรือ

mean = closing_price.mean()
std = closing_price.std()

newdf = df.query('@mean <= closing_price <= @std')

3

หากคุณกำลังจัดการกับค่าหลายค่าและหลายอินพุตคุณสามารถตั้งค่าฟังก์ชันใช้งานได้เช่นนี้ ในกรณีนี้จะกรองดาต้าเฟรมสำหรับตำแหน่ง GPS ที่อยู่ในช่วงที่กำหนด

def filter_values(lat,lon):
    if abs(lat - 33.77) < .01 and abs(lon - -118.16) < .01:
        return True
    elif abs(lat - 37.79) < .01 and abs(lon - -122.39) < .01:
        return True
    else:
        return False


df = df[df.apply(lambda x: filter_values(x['lat'],x['lon']),axis=1)]

1

แทนที่จะเป็นแบบนี้

df = df[(99 <= df['closing_price'] <= 101)]

คุณควรใช้สิ่งนี้

df = df[(df['closing_price']>=99 ) & (df['closing_price']<=101)]

เราต้องใช้ตัวดำเนินการลอจิกแบบบิตของ NumPy |, &, ~, ^ สำหรับการผสมคิวรี นอกจากนี้วงเล็บยังมีความสำคัญสำหรับลำดับความสำคัญของตัวดำเนินการ

สำหรับข้อมูลเพิ่มเติมคุณสามารถไปที่ลิงค์: การเปรียบเทียบมาสก์และตรรกะบูลีน


0

หากต้องโทรซ้ำ ๆ (สำหรับขอบเขตที่แตกต่างกันและ) งานจำนวนมากจะถูกทำซ้ำโดยไม่จำเป็น ในกรณีนี้มันเป็นประโยชน์ในการจัดเรียงกรอบ / ซีรีส์ครั้งเดียวแล้วใช้ ฉันวัดความเร็วได้สูงสุด 25x ดูด้านล่างpd.Series.between(l,r) lrpd.Series.searchsorted()

def between_indices(x, lower, upper, inclusive=True):
    """
    Returns smallest and largest index i for which holds 
    lower <= x[i] <= upper, under the assumption that x is sorted.
    """
    i = x.searchsorted(lower, side="left" if inclusive else "right")
    j = x.searchsorted(upper, side="right" if inclusive else "left")
    return i, j

# Sort x once before repeated calls of between()
x = x.sort_values().reset_index(drop=True)
# x = x.sort_values(ignore_index=True) # for pandas>=1.0
ret1 = between_indices(x, lower=0.1, upper=0.9)
ret2 = between_indices(x, lower=0.2, upper=0.8)
ret3 = ...

เกณฑ์มาตรฐาน

การวัดประเมินผลซ้ำ ( n_reps=100) ของpd.Series.between()เช่นเดียวกับวิธีการที่อยู่บนพื้นฐานpd.Series.searchsorted()สำหรับข้อโต้แย้งที่แตกต่างกันและlower upperใน MacBook Pro 2015 ของฉันที่มี Python v3.8.0 และ Pandas v1.0.3 โค้ดด้านล่างส่งผลให้เกิดผลลัพธ์ดังต่อไปนี้

# pd.Series.searchsorted()
# 5.87 ms ± 321 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
# pd.Series.between(lower, upper)
# 155 ms ± 6.08 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
# Logical expressions: (x>=lower) & (x<=upper)
# 153 ms ± 3.52 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
import numpy as np
import pandas as pd

def between_indices(x, lower, upper, inclusive=True):
    # Assumption: x is sorted.
    i = x.searchsorted(lower, side="left" if inclusive else "right")
    j = x.searchsorted(upper, side="right" if inclusive else "left")
    return i, j

def between_fast(x, lower, upper, inclusive=True):
    """
    Equivalent to pd.Series.between() under the assumption that x is sorted.
    """
    i, j = between_indices(x, lower, upper, inclusive)
    if True:
        return x.iloc[i:j]
    else:
        # Mask creation is slow.
        mask = np.zeros_like(x, dtype=bool)
        mask[i:j] = True
        mask = pd.Series(mask, index=x.index)
        return x[mask]

def between(x, lower, upper, inclusive=True):
    mask = x.between(lower, upper, inclusive=inclusive)
    return x[mask]

def between_expr(x, lower, upper, inclusive=True):
    if inclusive:
        mask = (x>=lower) & (x<=upper)
    else:
        mask = (x>lower) & (x<upper)
    return x[mask]

def benchmark(func, x, lowers, uppers):
    for l,u in zip(lowers, uppers):
        func(x,lower=l,upper=u)

n_samples = 1000
n_reps = 100
x = pd.Series(np.random.randn(n_samples))
# Sort the Series.
# For pandas>=1.0:
# x = x.sort_values(ignore_index=True)
x = x.sort_values().reset_index(drop=True)

# Assert equivalence of different methods.
assert(between_fast(x, 0, 1, True ).equals(between(x, 0, 1, True)))
assert(between_expr(x, 0, 1, True ).equals(between(x, 0, 1, True)))
assert(between_fast(x, 0, 1, False).equals(between(x, 0, 1, False)))
assert(between_expr(x, 0, 1, False).equals(between(x, 0, 1, False)))

# Benchmark repeated evaluations of between().
uppers = np.linspace(0, 3, n_reps)
lowers = -uppers
%timeit benchmark(between_fast, x, lowers, uppers)
%timeit benchmark(between, x, lowers, uppers)
%timeit benchmark(between_expr, x, lowers, uppers)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.