1
votes

Comment vérifier si un élément de la ligne Pandas df a une valeur avec un signe d'égalité (la valeur après le signe change)

J'ai un dataframe avec des conditions comme celle-ci:

list2 = ['goat=3','duck=2']

Et une liste avec une seule condition à vérifier par rapport au df comme:

list = ['goat','goat','duck','goat','duck']

A partir de cette liste, j'ai créé une autre liste qui montre le nombre d'occurrences de chaque élément de la liste1

  condition1 Condition2 Condition3 Condition4
0 duck>1          goat>2     sheep=0    chicken=0
1 duck=0          chicken=0  donkey>1   zebra>0

Pour cet exemple, je veux revenir à la ligne '0' dans le df puisque les conditions sont toutes remplies (note: l'absence de mouton et de poulet dans ma liste est égale à mouton = 0 et poulet = 0).

Le nombre de catégories dans les conditions (dans ce cas les animaux) sont tellement nombreux pour les définir à l'avance.

Comment pourrais-je vérifier et récupérer les lignes où les conditions sont remplies?

Merci


2 commentaires

Juste pour clarifier, vous voulez que le programme renvoie la ligne dans laquelle toutes les conditions ont été remplies, n'est-ce pas?


Exactement @RhysFlook


3 Réponses :


1
votes

Je ne peux donc pas penser à une solution simple pour cela, mais je commencerais par faire de cette deuxième liste un dictionnaire qui se lit comme suit.

true_rows = []
for row in df.iterrows():
    # row[0] would return the index, row[1] gets the rest of the row
    condition1 = row[1]['condition1']
    condition2 = row[1]['condition2']

    # pass the strings into the animal_counter function
    true_or_false = animal_counter(condition1, condition2)

    if true_or_false:
        true_rows.append(row[0])

Imaginons simplement que vous en ayez deux conditions pour l'instant, par exemple 'duck> 0' et 'goat> 2'.

Je créerais une fonction pour transformer les conditions du dataframe en conditions réelles.

def animal_counter(condition1, condition2):
    # I will just show one example here but you would have to make more conditionals
    all_conditions_met = False
    for condition in (condition1, condition2):
        if '>' in condition:
            # by splitting the condition by the conditional symbol
            # we get two useful parts, the animal name and the number
            # which we can compare against the dictionary to get a boolean result
            animal_num = condition.split('>')

        try:
            if dict1[animal_num[0]] > int(animal_num[1]):
                all_conditions_met = True
            else:
                # By putting this return statement here we end the function if one
                # condition returns as False
                all_conditions_met = False
                return all_conditions_met

        except Keyerror:
            if int(animal_num[1]) > 0:
                all_conditions_met = False
                return all_conditions_met
            else:
                all_conditions_met = True

    return all_conditions_met

Ensuite, je parcourirais le dataframe et prendrais chaque condition comme une chaîne.

dict1 = {'goat': 3, 'duck':2}

Cela devrait renvoyer une liste des index de toutes les lignes où les conditions sont remplies.

Il vous suffit de développer un peu pour faire cela fonctionne pour toutes les conditions possibles et plus de colonnes.


0 commentaires

1
votes

utilisez collections.Counter pour obtenir le nombre d'éléments dans la liste, puis appliquez une fonction qui vérifie votre condition à chaque ligne en utilisant pandas.DataFrame.apply

laissez votre Dataframe et votre liste être

df1['select'] = df.apply(lambda x: foo(x,lst),axis=1)
>>> print(df1[df1.select])
  condition1 condition2 condition3 condition4   select
0     duck>1     goat>2    sheep=0  chicken=0     True

fonction à passer pour appliquer

from collections import Counter
from collections import Counter
def bar(op,ctr,item):
    item = item.split(op)
    val = ctr.get(item[0])
    if item:
        if val:
            if op == '=':
                if int(val) < int(item[1]):
                    return False
            elif op == '>':
                if int(val) <= int(item[1]):
                    return False
        else:
            if int(item[1]) !=0:
                return False
    return True

def foo(row,lst):
    ctr = dict(Counter(lst))
    for item in list(row):
        if '=' in item:
            if not bar('=',ctr,item):
                return False  
                    
        elif '>' in item:
            if not bar('>',ctr,item):
                return False   
    return True
>>df
  condition1.   condition2  condition3  condition4
0     duck>1        goat>2     sheep=0   chicken=0
1     duck=0     chicken=0    donkey>1     zebra>0
2     duck=4     chicken=0    donkey>1     zebra>0

>>lst
['goat','goat','duck','goat','duck']

EDIT : code corrigé


1 commentaires

Pour la 2ème ligne du df, ses conditions ne sont pas remplies par la liste. Il devrait renvoyer False @Shijith



3
votes

Il existe d'autres moyens d'obtenir list2 , qui auraient été mieux adaptés à ce problème. Cette solution est uniquement basée sur vos variables:

1. Méthode simple (Bruteforce):

# make a dict out of list2, there are other ways  
# to get to the next step directly from list1
dict2 = dict(item.split('=') for item in list2)

# make a mapping of checker functions wrt operators
comparison_func = {'>': lambda x,y: x > y, 
                   '<': lambda x,y: x < y, 
                   '=': lambda x,y: x == y}

# create an empty list to keep provision for the case
# when conditions are matched in multiple rows
valid_rows = []

# Iterate over the dataframe
for row_ix, row_contents in df.iterrows():

    # set a flag for each row
    conditions_met = True

    # iterate through each column in the row
    for condition in row_contents:

        # check which operator is in the current column
        for operator in comparison_func:

            # if operator found, split by it, store the (animal, value) and break
            if operator in condition:
                animal, value = condition.split(operator)
                break
        # get the comparison function for the operator, name the function is_true
        is_true = comparison_func[operator]

        # check if the function evaluates to true with given values
        # dict2.get(animal,0) will give the value for that animal from dict2
        # if the animal is not in dict2 it will return default value 0
        if not is_true(int(dict2.get(animal,0)), int(value)):
            # if condition did not meet, set conditions_met False, break
            conditions_met = False
            break

    # outside the loop, if the conditions_met stays True throughout the previous loop
    # append the row index to the valid_rows
    if conditions_met:
        valid_rows.append(row_ix)

print(valid_rows)

Sortie:

>>> list2 = ['goat=3','duck=2']
>>> dict2 = dict(item.split('=') for item in list2)
>>> globals().update(dict2)

>>> def is_true(x):
...     if '>' in x:
...         animal, value = x.split('>')
...         return int(globals().get(animal, 0)) > int(value)
...     elif '<' in x:
...         animal, value = x.split('<')
...         return int(globals().get(animal, 0)) < int(value)
...     else:
...         animal, value = x.split('=')
...         return int(globals().get(animal, 0)) == int(value)

>>> df.applymap(is_true)
   condition1  Condition2  Condition3  Condition4
0        True        True        True        True
1       False        True       False       False

>>> df[df.applymap(is_true).all(1)].index
Int64Index([0], dtype='int64')

>>> df['conditions_met'] = df.applymap(is_true).all(1)

>>> df
  condition1 Condition2 Condition3 Condition4  conditions_met
0     duck>1     goat>2    sheep=0  chicken=0            True
1     duck=0  chicken=0   donkey>1    zebra>0           False

Plus tard, vous pouvez faire: p >

>>> df['conditions_met'] = df.index.isin(valid_rows)
>>> df
  condition1 Condition2 Condition3 Condition4  conditions_met
0     duck>1     goat>2    sheep=0  chicken=0            True
1     duck=0  chicken=0   donkey>1    zebra>0           False

2. Méthode intelligente:

[0]

EXPLICATION pour la méthode 1 :

list2 = ['goat=3','duck=2']
dict2 = dict(item.split('=') for item in list2)
comparison_func = {'>': lambda x,y: x > y, 
                   '<': lambda x,y: x < y, 
                   '=': lambda x,y: x == y}

valid_rows = []
for row_ix, row_contents in df.iterrows():
    conditions_met = True
    for condition in row_contents:
        for operator in comparison_func:
            if operator in condition:
                animal, value = condition.split(operator)
                break

        is_true = comparison_func[operator]
        if not is_true(int(dict2.get(animal,0)), int(value)):
            conditions_met = False
            break
    if conditions_met:
        valid_rows.append(row_ix)
print(valid_rows)

Explication de la méthode 2 :

globals () est une fonction intégrée python qui donne accès aux variables globales dans l'espace de noms, via un dictionnaire. Ici, je mets à jour l'espace de noms global avec dict2 , c'est-à-dire que maintenant les deux goat et duck existent en tant que variables globales, et peuvent accessible depuis l'intérieur d'autres fonctions pour comparaison.

df.applymap (func) applique une fonction à chaque élément d'un dataframe.

df.all (axis) vérifie si tout les valeurs sont évaluées à True sur une axe donnée .

Et c'est tout.


0 commentaires