4
votes

Gestion des valeurs NaN dans les pandas avec instruction conditionnelle

Je travaille avec certaines données pour lesquelles les données du code postal du client ne sont pas valides. En conséquence, je ne suis pas en mesure de mapper le CountryISOCode à son code postal, ce qui entraîne un NaN. Cependant, j'ai remarqué que pour tous les CountryISOCodes avec NaN, le CurrencyCode peut me fournir suffisamment pour résoudre le problème pour le moment.

J'ai consulté divers articles de Stackoverflow mais je ne trouve pas la solution à mon problème. J'ai essayé ...

IF countryISOCode IS NULL 
    AND currency = ‘GBP’ 
THEN CountryISOCode =  ‘GBR’
ELSE
IF countryISOCode IS NULL 
    AND currency = ‘EUR 
THEN CountryISOCode =  ‘IRE’
ELSE countryISOCode 
END

et quelques autres méthodes mais en vain ...

Ci-dessous, j'ai fourni une réplication des données que j'ai Je travaille avec

import pandas as pd
import numpy as np

data = [
    ['Steve', 'Invalid Postcode', 'GBP', np.nan ],
    ['Robyn', 'Invalid Postcode', 'EUR', np.nan],
    ['James', 'Valid Postcode', 'GBP', 'GBR'],
    ['Halo', 'Invalid Postcode', 'EUR', np.nan],
    ['Jesus', 'Valid Postcode', 'GBP', 'GBR']
    ]

df = pd.DataFrame(columns=["Name", "PostCode", "CurrencyCode", "CountryISOCode"], data=data)

Essentiellement, si je travaillais avec SQL, mon code serait le suivant.

def func(row):
    if row['CountryISOCode'] == np.nan & row['Currency'] == 'EUR':
        return 'IRE'
elif row['CountryISOCode'] == np.nan & row['Currency'] == 'GBP':
    return 'GBR'
else:
    return row['CountryISOCode']

df['CountryISOCode'] = df.apply(func, axis=1)

Toutes les idées ?


2 commentaires

Avez-vous essayé de remplacer & par et ? Il y a une différence entre les deux et je pense que vous voulez utiliser et ici. Vous pouvez en savoir plus sur la différence ici [ stackoverflow.com/questions/22646463/...


Le code fonctionne avec et mais ne semble pas résoudre le problème. Les données restent les mêmes.


5 Réponses :


2
votes

Vous pouvez utiliser np .select pour cela, qui vous permet de choisir dans une liste en fonction du résultat d'une liste de conditions:

m1 = df.CountryISOCode.isna()
m2 = df.CurrencyCode.eq('GBP')
m3 = df.CurrencyCode.eq('EUR')
df.loc[:,'CountryISOCode'] = np.select([m1&m2, m1&m3], ['GBP','IRE'], 
                                       default=df.CountryISOCode)

 Name          PostCode CurrencyCode CountryISOCode
0  Steve  Invalid Postcode          GBP            GBP
1  Robyn  Invalid Postcode          EUR            IRE
2  James    Valid Postcode          GBP            GBR
3   Halo  Invalid Postcode          EUR            IRE
4  Jesus    Valid Postcode          GBP            GBR


2 commentaires

Ce type de fonctionnement fonctionne mais il remplit une valeur GBP avec IRE qui ne fonctionnerait pas. Index 2


Merci d'avoir répondu à ma question! Votre solution a fonctionné.



2
votes

utilisez np.select () pour plusieurs conditions et plusieurs choix:

df['CountryISOCode']=np.select([(df.CurrencyCode=='GBP')&(df.CountryISOCode.isna()),\
                            (df.CurrencyCode=='EUR')&df.CountryISOCode.isna()],['GBR','IRE'],\
                           default=df.CountryISOCode)

    Name          PostCode CurrencyCode CountryISOCode
0  Steve  Invalid Postcode          GBP            GBR
1  Robyn  Invalid Postcode          EUR            IRE
2  James    Valid Postcode          GBP            GBR
3   Halo  Invalid Postcode          EUR            IRE
4  Jesus    Valid Postcode          GBP            GBR


1 commentaires

Merci d'avoir répondu à ma question! Votre solution a fonctionné.



3
votes

Vous pouvez utiliser fillna avec un dictionnaire spécifiant les mappages lorsque le code de devise est utile:

cmap = {'GBP': 'GBR', 'EUR': 'IRE'}
df['CountryISOCode'] = df['CountryISOCode'].fillna(df['CurrencyCode'].map(cmap))

print(df)

    Name          PostCode CurrencyCode CountryISOCode
0  Steve  Invalid Postcode          GBP            GBR
1  Robyn  Invalid Postcode          EUR            IRE
2  James    Valid Postcode          GBP            GBR
3   Halo  Invalid Postcode          EUR            IRE
4  Jesus    Valid Postcode          GBP            GBR


1 commentaires

Cette solution a résolu mon problème de la manière la plus simple ... Un changement mineur est qu'il devrait être cmap = {'GBP': 'GBR', 'EUR': 'IRE'}



1
votes

Alors que l'autre réponse utilisant np.select fonctionne, mon favori personnel utilise mask:

df['CountryISOCode'] = df['CountryISOCode'] \
    .mask(df['CountryISOCode'].isna() & df['Currency'].eq('GBP'), 'GBR') \
    .mask(df['CountryISOCode'].isna() & df['Currency'].eq('EUR'), 'IRE')


1 commentaires

Merci d'avoir répondu à ma question!.



3
votes

J'ajoute cette réponse car elle ajoute de la valeur à la question d'origine. La raison pour laquelle les instructions de comparaison ne fonctionnaient pas est que np.nan == np.nan ne fonctionnera pas. Vous pouvez vérifier l'identité de l'élément NaN mais pas l'égalité. Voir dans operator, float ("NaN") et np.nan pour en savoir plus détail. Cela dit, voici comment vous pouvez transformer le code d'origine pour qu'il fonctionne comme prévu.

import pandas as pd                                                                                                                                    
import numpy as np

raw_data = [
    ['Steve', 'Invalid Postcode', 'GBP', np.nan ],
    ['Robyn', 'Invalid Postcode', 'EUR', np.nan],
    ['James', 'Valid Postcode', 'GBP', 'GBR'],
    ['Halo', 'Invalid Postcode', 'EUR', np.nan],
    ['Jesus', 'Valid Postcode', 'GBP', 'GBR']
    ]

df = pd.DataFrame(columns=["Name", "PostCode", "Currency", "CountryISOCode"], data=raw_data)

def func(row):
    if row['CountryISOCode'] is np.nan and row['Currency'] == 'EUR':
        return 'IRE'
    elif row['CountryISOCode'] is np.nan and row['Currency'] == 'GBP':
        return 'GBR'
    else:
        return row['CountryISOCode']

df['CountryISOCode'] = df.apply(func, axis=1)

print(df)

Cependant, les autres réponses sont également excellentes.


0 commentaires