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

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])

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


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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.