J'ai 3 fichiers que je lis dans des dataframes ( https://pastebin.com/v7BnSH3s )
map_df: mappe les en-têtes data_file aux en-têtes codes_df
# code column to list
gender_codes = codes_df["gender_codes"].tolist()
# remove nan string
gender_codes = [gender_codes
for gender_codes in gender_codes
if str(gender_codes) != "nan"]
# check each value against code list
result_df = data_df.loc[(~data_df.Gender.isin(gender_codes))]
result_df = result_df.filter(items = ["Name","Gender"])
result_df.rename(columns = {"Gender":"Value"}, inplace = True)
result_df['Column'] = 'Gender'
code_df: Codes valides
Name Value Column Alex 99 Gender
data_df: les données réelles qui doivent être comparées aux codes
Name Gender Race Ethnicity Alex 99 1 7 Cindy 2 4 5 Tom 1 99 1
Problème:
Je dois confirmer que chaque valeur de chaque colonne de data_df est un code valide. Sinon, je dois écrire le Nom , la valeur invalide et le libellé de l'en-tête de colonne comme une nouvelle colonne. Donc, mon exemple data_df donnerait le dataframe suivant pour le contrôle gender_codes :
result_df:
gender_codes race_codes ethnicity_codes 1 1 1 2 2 2 3 3 3 4 4 4 NaN NaN 5 NaN NaN 6 NaN NaN 7
Contexte:
data_df . map_df autrement que pour savoir à quelles colonnes correspond les colonnes
quels codes. Cependant, si je peux incorporer cela dans mon script, ce serait
idéal. Ce que j'ai essayé:
J'envoie actuellement chaque colonne de code à une liste, en supprimant la chaîne nan , en effectuant la recherche avec loc et isin , puis en configurant le result_df ...
Field Name Code Name Gender gender_codes Race race_codes Ethnicity ethnicity_codes
Cela fonctionne mais est évidemment extrêmement primitif et ne s'adapte pas à mon ensemble de données. J'espère trouver une approche itérative et pythonique de ce problème.
MODIFIER: Ensemble de données modifié avec np.nan
3 Réponses :
Vous devrez prétraiter vos dataframes et définir une fonction de validation. Quelque chose comme ci-dessous:
1. Prétraitement
mdata['isValid'] = mdata.apply(isValid, axis=1) result = mdata[mdata.isValid == False] Out: result: Name Column Value isValid 0 Alex Gender 99 False 5 Tom Race 99 False
2. Fonction de validation
def isValid(row):
valid_list = validation_df[validation_df['Field Name'] == row.Column]['Valid Code'].tolist()
return row.Value in valid_list
3. Validation
# call melt() to convert columns to rows
mcodes = codes_df.melt(
value_vars=list(codes_df.columns),
var_name='Code Name',
value_name='Valid Code').dropna()
mdata = data_df.melt(
id_vars='Name',
value_vars=list(data_df.columns[1:]),
var_name='Column',
value_name='Value')
validation_df = mcodes.merge(map_df, on='Code Name')
Out:
mcodes:
Code Name Valid Code
0 gender_codes 1
1 gender_codes 2
7 race_codes 1
8 race_codes 2
9 race_codes 3
10 race_codes 4
14 ethnicity_codes 1
15 ethnicity_codes 2
16 ethnicity_codes 3
17 ethnicity_codes 4
18 ethnicity_codes 5
19 ethnicity_codes 6
20 ethnicity_codes 7
mdata:
Name Column Value
0 Alex Gender 99
1 Cindy Gender 2
2 Tom Gender 1
3 Alex Race 1
4 Cindy Race 4
5 Tom Race 99
6 Alex Ethnicity 7
7 Cindy Ethnicity 5
8 Tom Ethnicity 1
validation_df:
Code Name Valid Code Field Name
0 gender_codes 1 Gender
1 gender_codes 2 Gender
2 race_codes 1 Race
3 race_codes 2 Race
4 race_codes 3 Race
5 race_codes 4 Race
6 ethnicity_codes 1 Ethnicity
7 ethnicity_codes 2 Ethnicity
8 ethnicity_codes 3 Ethnicity
9 ethnicity_codes 4 Ethnicity
10 ethnicity_codes 5 Ethnicity
11 ethnicity_codes 6 Ethnicity
12 ethnicity_codes 7 Ethnicity
C'est aussi une bonne implémentation. J'ai laissé tomber le .dropna () de mcodes afin de transmettre les valeurs NaN. Merci!
Je reformaterais vos données sous différentes formes
ddf # Melted dataframe to help match the final output
Name Column Value
0 Alex Gender 99
1 Cindy Gender 2
2 Tom Gender 1
3 Alex Race 1
4 Cindy Race 4
5 Tom Race 99
6 Alex Ethnicity 7
7 Cindy Ethnicity 5
8 Tom Ethnicity 1
c # Series of membership sets
ethnicity_codes {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0}
gender_codes {1.0, 2.0, 3.0, 4.0}
race_codes {1.0, 2.0, 3.0, 4.0}
dtype: object
m # Just a dictionary with the same content as `map_df`
{'Gender': 'gender_codes',
'Race': 'race_codes',
'Ethnicity': 'ethnicity_codes'}
m = dict(map_df.itertuples(index=False))
c = code_df.T.stack().groupby(level=0).apply(set)
ddf = data_df.melt('Name', var_name='Column', value_name='Value')
ddf[[val not in c[col] for val, col in zip(ddf.Value, ddf.Column.map(m))]]
Name Column Value
0 Alex Gender 99
5 Tom Race 99
Merci! Fonctionne très bien. Après coup, il est possible que data_df ait des valeurs nulles. Si tel est le cas, je pourrais ajouter des critères à la compréhension de la liste " notna () ", n'est-ce pas?
vous pourriez ... mais si vous donniez un exemple, je pourrais probablement trouver quelque chose de plus propre.
gotcha, vient d'ajouter quelques valeurs NaN à data_df ( pastebin.com/v7BnSH3s ). Merci!
L'astuce consiste à obtenir le dictionnaire ou une série d'ensembles acceptables. Je peux ajouter np.nan à ethnicity_codes avec c = code_df.T.stack (). Groupby (level = 0) .apply (set); c.at ['ethnicity_codes'] | = {np.nan}
Ah gotcha, ça a du sens. Je pensais à une solution de contournement beaucoup plus compliquée. Merci encore!
m, df1 = dict(map_df.values), data_df.set_index('Name')
df1[df1.apply(lambda x:~x.isin(code_df[m[x.name]]))].stack().reset_index()
Out:
Name level_1 0
0 Alex Gender 99.0
1 Tom Race 99.0
donc les colonnes dans
codes_dfdevraient être pargender_codeetc. et non pargender_listetc.?@ anky_91, ouais, je viens de le réparer. Merci