Проверьте, что строки в фрейме данных pandas содержат определенные значения

#python #pandas

Вопрос:

Мне интересно, как убедиться, что все строки во фрейме данных содержат определенный набор значений.

Например:

 VALUES = [1, 2]
df_no = pd.DataFrame(
    {
        "a": [1],
        "b": [1],
    }
)
df_yes = pd.DataFrame(
    {
        "a": [1],
        "b": [2],
        "c": [3],
    }
)
 

Здесь df_no не содержатся значения VALUES в каждой из его строк, в то df_yes время как это так.

Подход заключается в следующем:

 # check df_no
all(
    [
        all(value in row for value in VALUES)
        for row in df_no.apply(lambda x: x.unique(), axis=1)
    ]
)
# returns False

# check df_yes 
all(
    [
        all(value in row for value in VALUES)
        for row in df_yes.apply(lambda x: x.unique(), axis=1)
    ]
)

# returns True
 

Я чувствую, что подходы здесь могут быть настолько ясными, и что может быть более идиоматический способ действий.

Ответ №1:

Использование issubset в понимании генератора:

 s = set(VALUES)
print (all(s.issubset(x) for x in df_no.to_numpy()))
False


s = set(VALUES)
print (all(s.issubset(x) for x in df_yes.to_numpy()))
True
 

Что быстрее? Зависит от данных:

 VALUES = [1, 2]

df = pd.DataFrame(
    {
        "a": [1,2,8],
        "b": [2,8,2],
        "c": [3,1,1],
    }
)

#30k rows
df = pd.concat([df] * 10000, ignore_index=True)
print (df)


In [171]: %%timeit
     ...: s = set(VALUES)
     ...: all(s.issubset(x) for x in df.to_numpy())
     ...: 
     ...: 
55.9 ms ± 2.77 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [172]: %%timeit
     ...: vals = set(VALUES)
     ...: df.apply(vals.issubset, axis=1).all()
     ...: 
     ...: 
211 ms ± 1.38 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
 

 #3k rows
df = pd.concat([df] * 1000, ignore_index=True)
print (df)



In [174]: %%timeit
     ...: s = set(VALUES)
     ...: all(s.issubset(x) for x in df.to_numpy())
     ...: 
     ...: 
5.46 ms ± 76.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [175]: %%timeit
     ...: vals = set(VALUES)
     ...: df.apply(vals.issubset, axis=1).all()
     ...: 
     ...: 
21.5 ms ± 107 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

    
 

Комментарии:

1. @baxx — Это неправильный ответ 🙁 Работает над правильным.

2. @baxx — Он не тестировал каждую строку отдельно. Он проверил все данные вместе

3. ах, конечно, да, важно, чтобы это была каждая строка

4. предпочтительнее ли применять list comp? Я не уверен, есть ли какие-либо оптимизации (или медлительность) с применением, о которых я не знаю

5. теперь это более или менее похоже на мой ответ 🙁

Ответ №2:

Вы можете использовать наборы python и issubset :

 vals = set(VALUES)
df_yes.apply(lambda x: vals.issubset(set(x)), axis=1).all()
 

более короткая версия:

 vals = set(VALUES)
df_yes.apply(vals.issubset, axis=1).all()