dtypes sujam as coisas ao mudar no eixo um (colunas)

9

Considere o quadro de dados df

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

df

   A  B
0  1  X
1  2  Y

Se eu mudar axis=0(o padrão)

df.shift()

     A    B
0  NaN  NaN
1  1.0    X

Empurra todas as linhas para baixo uma linha, conforme o esperado.

Mas quando eu mudo axis=1

df.shift(axis=1)

    A    B
0 NaN  NaN
1 NaN  NaN

Tudo é nulo quando eu esperava

     A  B
0  NaN  1
1  NaN  2

Eu entendo por que isso aconteceu. Pois axis=0, o Pandas está operando coluna por coluna, onde cada coluna é única dtypee, ao mudar, existe um protocolo claro sobre como lidar com o NaNvalor introduzido no início ou no final. Mas, ao mudar axis=1, introduzimos a ambiguidade potencial de dtypeuma coluna para a seguinte. Nesse caso, estou tentando forçar int64uma objectcoluna e o Pandas decide apenas anular os valores.

Isso se torna mais problemático quando o dtypessão int64efloat64

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

df

   A    B
0  1  1.0
1  2  2.0

E a mesma coisa acontece

df.shift(axis=1)

    A   B
0 NaN NaN
1 NaN NaN

Minha pergunta

Quais são as boas opções para criar um quadro de dados que é deslocado ao longo axis=1do qual o resultado alterou valores e tipos?

Para o caso int64/ float64, o resultado seria semelhante a:

df_shifted

     A  B
0  NaN  1
1  NaN  2

e

df_shifted.dtypes

A    object
B     int64
dtype: object

Um exemplo mais abrangente

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

Deve ficar assim

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
fonte
Parece um bug para mim, o que acontece se você criar os tipos de todas as colunas object?
EdChum
Funciona. Eu já tenho algumas soluções. Estou apenas cutucando a comunidade para algumas idéias.
piRSquared
Eu apresentar isso como um problema, eles deveriam pelo menos oferecer uma opção para dtipo promoção para um dtipo mista tais comoobject
EdChum
Eu farei isso agora.
piRSquared
11
@ EdChum-ReinstateMonica Espere um minuto! A mudança acontece ao longo de blocks>. <Use isso em vez disso e vejadf = pd.DataFrame(dict(A=[1, 2], B=[3., 4.], C=['X', 'Y'], D=[5., 6.], E=[7, 8], F=['W', 'Z']))
piRSquared

Respostas:

7

Acontece que os pandas estão mudando sobre blocos de similares dtypes

Definir dfcomo

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

Ele mudará os números inteiros para a próxima coluna inteira, os flutuadores para a próxima coluna flutuante e os objetos para a próxima coluna de objetos

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

Não sei se é uma boa ideia, mas é isso que está acontecendo.


Abordagens

astype(object) primeiro

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

Vai fazer isso 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

Embora eu provavelmente faria isso

pd.DataFrame([
    (np.nan, *t[:-1]) for t in
    df.itertuples(index=False, name=None)
], columns=[*df])
piRSquared
fonte
4
Este é definitivamente um bug para mim, Isso invalida toda a ponto de ter colunas com chave e mudando por N posiciona coluna-wise
EdChum
11
Vou postar um problema após a minha reunião.
piRSquared
Se tudo é do strtipo, então funciona corretamente, se você faz o mesmo neste df, df = pd.DataFrame(dict(C=['X', 'Y'], D=[5., 6.], E=[7, 8], F=['W', 'Z']))ele muda a 'XY'coluna para a 'F'coluna, isso é definitivamente errado para mim, minha versão do pandas é 0.24.2: ela deve fazer dtypepromoção e não alterar as colunas em tais um caminho
EdChum 5/11/19
Emissão de
código
1

Eu tentei usar um numpymétodo. O método funciona desde que você mantenha seus dados em uma matriz 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)

Mas quando você chama o DataFrameconstrutor, todas as colunas são convertidas em, objectembora os valores na matriz sejam 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
fonte