dtypes détruit les choses lors du déplacement sur l'axe un (colonnes)

9

Tenez compte de la trame de données df

df = pd.DataFrame(dict(A=[1, 2], B=['X', 'Y']))

df

   A  B
0  1  X
1  2  Y

Si je me déplace axis=0(par défaut)

df.shift()

     A    B
0  NaN  NaN
1  1.0    X

Il pousse toutes les lignes vers le bas d'une ligne comme prévu.

Mais quand je me déplace axis=1

df.shift(axis=1)

    A    B
0 NaN  NaN
1 NaN  NaN

Tout est nul quand je m'y attendais

     A  B
0  NaN  1
1  NaN  2

Je comprends pourquoi cela s'est produit. Pour axis=0, Pandas fonctionne colonne par colonne où chaque colonne est unique dtypeet lors du déplacement, il existe un protocole clair sur la façon de traiter la NaNvaleur introduite au début ou à la fin. Mais lorsque axis=1nous nous déplaçons, nous introduisons une ambiguïté potentielle d' dtypeune colonne à l'autre. Dans ce cas, j'essaie de forcer int64dans une objectcolonne et Pandas décide de simplement annuler les valeurs.

Cela devient plus problématique lorsque le dtypessont int64etfloat64

df = pd.DataFrame(dict(A=[1, 2], B=[1., 2.]))

df

   A    B
0  1  1.0
1  2  2.0

Et la même chose se produit

df.shift(axis=1)

    A   B
0 NaN NaN
1 NaN NaN

Ma question

Quelles sont les bonnes options pour créer une trame de données qui est décalée le long axis=1de laquelle le résultat a décalé les valeurs et les dtypes?

Pour le cas int64/ float64, le résultat ressemblerait à:

df_shifted

     A  B
0  NaN  1
1  NaN  2

et

df_shifted.dtypes

A    object
B     int64
dtype: object

Un exemple plus complet

df = pd.DataFrame(dict(A=[1, 2], B=[1., 2.], C=['X', 'Y'], D=[4., 5.], E=[4, 5]))

df

   A    B  C    D  E
0  1  1.0  X  4.0  4
1  2  2.0  Y  5.0  5

Devrait ressembler à ceci

df_shifted

     A  B    C  D    E
0  NaN  1  1.0  X  4.0
1  NaN  2  2.0  Y  5.0

df_shifted.dtypes

A     object
B      int64
C    float64
D     object
E    float64
dtype: object
piRSquared
la source
Cela ressemble à un bug pour moi, que se passe-t-il si vous créez les dtypes de toutes les colonnes object?
EdChum
Ça marche. J'ai déjà quelques contournements. Je pousse juste la communauté pour quelques idées.
piRSquared
Je classerais cela comme un problème, ils devraient au moins offrir une option pour la promotion de dtype vers un dtype mixte tel queobject
EdChum
Je vais le faire maintenant.
piRSquared
1
@ EdChum-ReinstateMonica Attendez une minute! Le changement se produit sur blocks>. <Utilisez-le à la place et voyezdf = pd.DataFrame(dict(A=[1, 2], B=[3., 4.], C=['X', 'Y'], D=[5., 6.], E=[7, 8], F=['W', 'Z']))
piRSquared

Réponses:

7

Il s'avère que les Pandas se déplacent sur des blocs de dtypes

Définir dfcomme

df = pd.DataFrame(dict(
    A=[1, 2], B=[3., 4.], C=['X', 'Y'],
    D=[5., 6.], E=[7, 8], F=['W', 'Z']
))

df

#  i    f  o    f  i  o
#  n    l  b    l  n  b
#  t    t  j    t  t  j
#
   A    B  C    D  E  F
0  1  3.0  X  5.0  7  W
1  2  4.0  Y  6.0  8  Z

Il déplacera les entiers vers la colonne entière suivante, les flottants vers la colonne flottante suivante et les objets vers la colonne objet suivante

df.shift(axis=1)

    A   B    C    D    E  F
0 NaN NaN  NaN  3.0  1.0  X
1 NaN NaN  NaN  4.0  2.0  Y

Je ne sais pas si c'est une bonne idée, mais c'est ce qui se passe.


Approches

astype(object) première

dtypes = df.dtypes.shift(fill_value=object)
df_shifted = df.astype(object).shift(1, axis=1).astype(dtypes)

df_shifted

     A  B    C  D    E  F
0  NaN  1  3.0  X  5.0  7
1  NaN  2  4.0  Y  6.0  8

transpose

Le fera object

dtypes = df.dtypes.shift(fill_value=object)
df_shifted = df.T.shift().T.astype(dtypes)

df_shifted

     A  B    C  D    E  F
0  NaN  1  3.0  X  5.0  7
1  NaN  2  4.0  Y  6.0  8

itertuples

pd.DataFrame([(np.nan, *t[1:-1]) for t in df.itertuples()], columns=[*df])

     A  B    C  D    E  F
0  NaN  1  3.0  X  5.0  7
1  NaN  2  4.0  Y  6.0  8

Bien que je ferais probablement ça

pd.DataFrame([
    (np.nan, *t[:-1]) for t in
    df.itertuples(index=False, name=None)
], columns=[*df])
piRSquared
la source
4
C'est définitivement un bug pour moi, cela invalide le fait d'avoir des colonnes à clés et un décalage de N positions par colonne
EdChum
1
Je publierai un problème après ma réunion.
piRSquared
Si ce sont tous des strdytpes, alors cela fonctionne correctement, si vous faites la même chose sur ce df, df = pd.DataFrame(dict(C=['X', 'Y'], D=[5., 6.], E=[7, 8], F=['W', 'Z']))cela déplace la 'XY'colonne complètement vers la 'F'colonne, c'est définitivement faux pour moi, ma version pandas est 0.24.2, elle devrait faire de la dtypepromotion et ne pas déplacer les colonnes dans un tel un moyen
EdChum
Numéro ouvert
piRSquared
1

J'ai essayé d'utiliser une numpyméthode. La méthode fonctionne tant que vous conservez vos données dans un tableau numpy:

def shift_df(data, n):
    shifted = np.roll(data, n)
    shifted[:, :n] = np.NaN

    return shifted

shifted(df, 1)

array([[nan, 1, 1.0, 'X', 4.0],
       [nan, 2, 2.0, 'Y', 5.0]], dtype=object)

Mais lorsque vous appelez le DataFrameconstructeur, toutes les colonnes sont converties en objectbien que les valeurs du tableau soient float, int, object:

def shift_df(data, n):
    shifted = np.roll(data, n)
    shifted[:, :n] = np.NaN
    shifted = pd.DataFrame(shifted)

    return shifted

print(shift_df(df, 1),'\n')
print(shift_df(df, 1).dtypes)

     0  1  2  3  4
0  NaN  1  1  X  4
1  NaN  2  2  Y  5 

0    object
1    object
2    object
3    object
4    object
dtype: object
Erfan
la source