2
votes

Combinez deux colonnes en donnant la priorité à la première

De cette question a>, j'ai deux matrices et je cherche à les fusionner de telle manière que j'ai laissé la jointure de dfB sur dfA en remplaçant les valeurs NaN par des valeurs non NaN partout où je les ai.

Autrement dit,

>>> new["value_z"] = None
>>> new.head()
  name    geo   zip  date  value_x  value_y  value value_z
0    A  state    01  2009      NaN      1.0    NaN    None
1    B  state    01  2010      NaN      NaN    NaN    None
2    C  state    01  2011      NaN      NaN    NaN    None
3    D  state    01  2012      NaN      4.0    NaN    None
4    E  state    01  2013      NaN      5.0    NaN    None

>>> new["value2"] = new.apply(lambda r: r.value_z or r.value_y, axis=1)
>>> new.head()
  name    geo   zip  date  value_x  value_y  value value_z   value2
0    A  state    01  2009      NaN      1.0    NaN    None      1.0
1    B  state    01  2010      NaN      NaN    NaN    None      NaN
2    C  state    01  2011      NaN      NaN    NaN    None      NaN
3    D  state    01  2012      NaN      4.0    NaN    None      4.0
4    E  state    01  2013      NaN      5.0    NaN    None      5.0

En les fusionnant, je vois:

>>> new["value"] = new.apply(lambda r: r.value_x or r.value_y, axis=1)
>>> new.head()
  name    geo   zip  date  value_x  value_y  value
0    A  state    01  2009      NaN      1.0    NaN
1    B  state    01  2010      NaN      NaN    NaN
2    C  state    01  2011      NaN      NaN    NaN
3    D  state    01  2012      NaN      4.0    NaN
4    E  state    01  2013      NaN      5.0    NaN

Je ne peux pas être sûr que value_y est toujours numérotée et value_x est toujours NaN. Mais je veux une valeur fusionnée, appelez-la valeur qui correspond à la valeur qui n'est pas NaN. J'essaye ceci:

>>> new = pd.merge(dfA,dfB,on=["s_name","geo", "geoid", "date"],how="left")
>>> new.head()
  name    geo   zip  date  value_x  value_y
0    A  state    01  2009      NaN      1.0
1    B  state    01  2010      NaN      NaN
2    C  state    01  2011      NaN      NaN
3    D  state    01  2012      NaN      4.0
4    E  state    01  2013      NaN      5.0

Oh non.

Cela a du sens que NaN devrait se propager, mais ce n'est pas ce que je recherche. Je voudrais une logique qui renverrait celui qui est présent, pas de retour NaN si l'un ou l'autre est présent.

J'aimerais la logique que None me donne. Vous pouvez voir:

>>> dfA
  s_name  geo    zip  date value
0      A  zip  60601  2010   NaN  # In the earlier question, this was None
1      B  zip  60601  2010   NaN  # rather than NaN, which was
2      C  zip  60601  2010   NaN  # a mistake.
3      D  zip  60601  2010   NaN

>>> dfB
  s_name  geo    zip  date  value
0      A  zip  60601  2010    1.0
1      B  zip  60601  2010    NaN
3      D  zip  60601  2010    4.0

La logique qui crée la valeur2 est le comportement que je recherche, pas la valeur .

Quelle est la meilleure façon de procéder?


3 commentaires

Que faire s'il existe une valeur autre que NaN pour 'value_x' et 'value_y' ?


J'ai une préférence pour value_x, mais en théorie cela ne devrait pas arriver. Si vous pouvez résoudre les deux problèmes, c'est parfait.


vouliez-vous dire cela? df.value_x = df.value_x.fillna (df.value_y) ?


3 Réponses :


0
votes

Cela fonctionne techniquement en martelant la logique, mais c'est moche et ressemble à un hack (je crois que cela donne la préférence à value_x en raison d'un court-circuit de l'opérateur?):

>>> new["value3"] = new.apply(lambda r: (not(pd.isna(r.value_x)) or r.value_y) or (r.value_x or not(pd.isna(r.value_y))), axis=1)

>>> new.head()
  name    geo   zip  date  value_x  value_y  value value_z   value2 value3
0    A  state    01  2009      NaN      1.0    NaN    None      1.0    1.0
1    B  state    01  2010      NaN      NaN    NaN    None      NaN    NaN
2    C  state    01  2011      NaN      NaN    NaN    None      NaN    NaN
3    D  state    01  2012      NaN      4.0    NaN    None      4.0    4.0
4    E  state    01  2013      NaN      5.0    NaN    None      5.0    5.0


0 commentaires

3
votes

combine_first fonctionnera après merge :

dfC = pd.merge(dfA, dfB, on=["s_name", "geo", "zip", "date"], how="left")
dfC['value_x'] = dfC['value_x'].combine_first(dfC.pop('value_y'))
dfC

  s_name  geo    zip  date  value_x
0      A  zip  60601  2010      1.0
1      B  zip  60601  2010      NaN
2      C  zip  60601  2010      NaN
3      D  zip  60601  2010      4.0

combine_first donne la préférence à "value_x" over " value_y ". Vous pouvez également écrire ceci comme suit:

dfC = pd.merge(dfA, dfB, on=["s_name", "geo", "zip", "date"], how="left")
dfC['value'] = dfC.pop('value_x').combine_first(dfC.pop('value_y'))
dfC

  s_name  geo    zip  date  value
0      A  zip  60601  2010    1.0
1      B  zip  60601  2010    NaN
2      C  zip  60601  2010    NaN
3      D  zip  60601  2010    4.0


3 commentaires

Je ne savais pas pour combine_first , merci. :)


C'est intelligent. Est-ce considéré comme idiomatique?


@Mittenchops Oui, je dirais que combine_first et fillna sont tous deux également de bons choix pour combiner deux séries et remplir des NaN dans le processus. Je suis allé avec combine_first car c'était dans l'autre message que vous avez lié.



4
votes

si vous avez une préférence pour value_x , vous pouvez essayer:

df.value_x=df.value_x.fillna(df.pop('value_y'))

>>df
   name geo    zip  date    value_x
0   A   state   1   2009    1.0
1   B   state   1   2010    NaN
2   C   state   1   2011    NaN
3   D   state   1   2012    4.0
4   E   state   1   2013    5.0

ou:

df.value_x = df.value_x.fillna(df.value_y)
df.pop('value_y')

p >


0 commentaires