Como pegar fatias de coluna do dataframe nos pandas

264

Carrego alguns dados de aprendizado de máquina de um arquivo CSV. As duas primeiras colunas são observações e as colunas restantes são recursos.

Atualmente, faço o seguinte:

data = pandas.read_csv('mydata.csv')

o que dá algo como:

data = pandas.DataFrame(np.random.rand(10,5), columns = list('abcde'))

Eu gostaria de cortar esta trama de dados em duas dataframes: um contendo as colunas ae be um contendo as colunas c, de e.

Não é possível escrever algo como

observations = data[:'c']
features = data['c':]

Não sei ao certo qual é o melhor método. Eu preciso de um pd.Panel?

A propósito, acho a indexação do quadro de dados bastante inconsistente: data['a']é permitido, mas data[0]não é. Por outro lado, data['a':]não é permitido, mas data[0:]é. Existe uma razão prática para isso? Isso é realmente confuso se as colunas são indexadas por Int, já quedata[0] != data[0:1]

cpa
fonte
3
O DataFrame é inerentemente um objeto semelhante a um ditado quando você faz df, [...] no entanto, algumas conveniências, por exemplo, df[5:10]foram adicionadas para a seleção de linhas ( pandas.pydata.org/pandas-docs/stable/… )
Wes McKinney
1
Então, o que essa inconsistência é uma decisão de design em favor da conveniência? Tudo bem, mas definitivamente precisa ser mais explícito para iniciantes!
Cpa
3
A consideração de design da conveniência de suporte torna a curva de aprendizado muito acentuada. Eu gostaria que houvesse uma documentação melhor para o começo, apenas apresentando uma interface consistente. Por exemplo, concentre-se apenas na interface ix.
Yu Shen

Respostas:

243

Resposta 2017 - pandas 0,20: .ix está obsoleto. Use .loc

Veja a reprovação nos documentos

.locusa indexação baseada em rótulo para selecionar linhas e colunas. Os rótulos são os valores do índice ou das colunas. Fatiar com .locinclui o último elemento.

Vamos supor que temos uma trama de dados com as seguintes colunas:
foo, bar, quz, ant, cat, sat, dat.

# selects all rows and all columns beginning at 'foo' up to and including 'sat'
df.loc[:, 'foo':'sat']
# foo bar quz ant cat sat

.locaceita a mesma notação de fatia que as listas Python fazem para linhas e colunas. Notação de fatia sendostart:stop:step

# slice from 'foo' to 'cat' by every 2nd column
df.loc[:, 'foo':'cat':2]
# foo quz cat

# slice from the beginning to 'bar'
df.loc[:, :'bar']
# foo bar

# slice from 'quz' to the end by 3
df.loc[:, 'quz'::3]
# quz sat

# attempt from 'sat' to 'bar'
df.loc[:, 'sat':'bar']
# no columns returned

# slice from 'sat' to 'bar'
df.loc[:, 'sat':'bar':-1]
sat cat ant quz bar

# slice notation is syntatic sugar for the slice function
# slice from 'quz' to the end by 2 with slice function
df.loc[:, slice('quz',None, 2)]
# quz cat dat

# select specific columns with a list
# select columns foo, bar and dat
df.loc[:, ['foo','bar','dat']]
# foo bar dat

Você pode cortar por linhas e colunas. Por exemplo, se você tem 5 linhas com rótulos v, w, x, y,z

# slice from 'w' to 'y' and 'foo' to 'ant' by 3
df.loc['w':'y', 'foo':'ant':3]
#    foo ant
# w
# x
# y
Ted Petrou
fonte
se você estiver usando apply com lambda row, como em: df['newcol'] = df.apply(lambda row: myfunc(row), axis=1) então você pode myfunc(row){... use row['foo':'ant']. por exemplo (de acordo com esta resposta do StackOverflow ), dentro de myfuncvocê pode avaliar se algum deles não é numérico:row['foo':'ant'].apply(lambda x: isinstance(x, str)).any()
pashute
4
.ilocdeve ser usado agora, em vez de .loc. Corrija isso, e eu vou votar.
craned
1
@craned - isso não está correto. Na documentação do Pandas: .loc é principalmente baseado em rótulo, mas também pode ser usado com uma matriz booleana. .loc aumentará KeyError quando os itens não forem encontrados. Uma declaração semelhante é feita sobre .iloc, exceto se refere especificamente à fatia baseada em índice. Em outras palavras, neste exemplo, ele usou a indexação baseada em rótulo e .loc é a escolha correta (basicamente a única opção). Se você deseja dividir por posição - linhas 5:10, por exemplo, use .iloc
user2103050
149

Nota: .ix está obsoleto desde o Pandas v0.20. Você deve usar .locou .iloc, conforme apropriado.

O índice DataFrame.ix é o que você deseja acessar. É um pouco confuso (eu concordo que a indexação do Pandas às vezes é desconcertante!), Mas o seguinte parece fazer o que você deseja:

>>> df = DataFrame(np.random.rand(4,5), columns = list('abcde'))
>>> df.ix[:,'b':]
      b         c         d         e
0  0.418762  0.042369  0.869203  0.972314
1  0.991058  0.510228  0.594784  0.534366
2  0.407472  0.259811  0.396664  0.894202
3  0.726168  0.139531  0.324932  0.906575

onde .ix [fatia de linha, fatia de coluna] é o que está sendo interpretado. Mais sobre a indexação do Pandas aqui: http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-advanced

Karmel
fonte
5
Cuidadoso que varia em pandas incluem ambos os pontos finais, ou seja>>>data.ix[:, 'a':'c'] a b c 0 0.859192 0.881433 0.843624 1 0.744979 0.427986 0.177159
gafanhoto
21
Várias colunas da cabine passam assim:df.ix[:,[0,3,4]]
user602599
3
@ Karmel: Parece um erro de copiar / colar na saída acima. Talvez você quis dizer df.ix[:,'b':'e']?
ChaimG
6
É melhor usar locem vez de ix: stackoverflow.com/a/31593712/4323
John Zwinck
5
Respostas antigas como essa precisam ser excluídas. .ix está obsoleto e nunca deve ser usado.
Ted Petrou
75

Vamos usar o conjunto de dados titânico do pacote seaborn como um exemplo

# Load dataset (pip install seaborn)
>> import seaborn.apionly as sns
>> titanic = sns.load_dataset('titanic')

usando os nomes das colunas

>> titanic.loc[:,['sex','age','fare']]

usando os índices da coluna

>> titanic.iloc[:,[2,3,6]]

using ix (Mais antigo que o Pandas <.20 versão)

>> titanic.ix[:,[‘sex’,’age’,’fare’]]

ou

>> titanic.ix[:,[2,3,6]]

usando o método reindex

>> titanic.reindex(columns=['sex','age','fare'])
Shankar ARUL - jupyterdata.com
fonte
6
Nos pandas 0,20: .ixestá obsoleto.
Shihe Zhang
aviso de Passing list-likes to .loc or [] with any missing label will raise KeyError in the future, you can use .reindex() as an alternative.df.loc[:, some_list_of_columns]
reprovação
35

Além disso, dado um DataFrame

dados

como no seu exemplo, se você deseja extrair apenas as colunas a e d (da 1ª e da 4ª coluna), o iloc mothod do quadro de dados do pandas é o que você precisa e pode ser usado com muita eficiência. Tudo o que você precisa saber é o índice das colunas que deseja extrair. Por exemplo:

>>> data.iloc[:,[0,3]]

Darei à você

          a         d
0  0.883283  0.100975
1  0.614313  0.221731
2  0.438963  0.224361
3  0.466078  0.703347
4  0.955285  0.114033
5  0.268443  0.416996
6  0.613241  0.327548
7  0.370784  0.359159
8  0.692708  0.659410
9  0.806624  0.875476
moldavo
fonte
25

Você pode dividir as colunas de a DataFrameconsultando os nomes de cada coluna em uma lista, da seguinte maneira:

data = pandas.DataFrame(np.random.rand(10,5), columns = list('abcde'))
data_ab = data[list('ab')]
data_cde = data[list('cde')]
Brendan Wood
fonte
Portanto, se eu quiser todos os dados começando na coluna 'b', preciso encontrar o índice de 'b' em data.columns e fazer data [data.columns [1:]]? Essa é a maneira canônica de operar?
Cpa
1
Quer dizer que você deseja selecionar todas as colunas de 'b' em diante?
Brendan Wood
Sim ou selecionando todas as colunas em um determinado intervalo.
Cpa
Eu sou muito novo em pandas, então não posso falar sobre o que é considerado canônico. Eu faria isso como você disse, mas use a get_locfunção data.columnspara determinar o índice da coluna 'b' ou qualquer outra coisa.
Brendan Wood
20

E se você veio aqui procurando fatiar duas faixas de colunas e combiná-las (como eu), pode fazer algo como

op = df[list(df.columns[0:899]) + list(df.columns[3593:])]
print op

Isso criará um novo quadro de dados com as primeiras 900 colunas e (todas) colunas> 3593 (supondo que você tenha cerca de 4000 colunas no seu conjunto de dados).

user2023507
fonte
Ótimo, alguém já tentou isso ... Eu estava pensando, esse 0: 899 que obtém as primeiras 900 colunas .. por que eles fizeram assim? Isso não parece o Python. Ao usar faixas em python é sempre 'até' não 'até e incluídos'
zwep
14

Veja como você pode usar métodos diferentes para fatiar seletivamente as colunas, incluindo o rótulo seletivo, o índice e o intervalo seletivo.

In [37]: import pandas as pd    
In [38]: import numpy as np
In [43]: df = pd.DataFrame(np.random.rand(4,7), columns = list('abcdefg'))

In [44]: df
Out[44]: 
          a         b         c         d         e         f         g
0  0.409038  0.745497  0.890767  0.945890  0.014655  0.458070  0.786633
1  0.570642  0.181552  0.794599  0.036340  0.907011  0.655237  0.735268
2  0.568440  0.501638  0.186635  0.441445  0.703312  0.187447  0.604305
3  0.679125  0.642817  0.697628  0.391686  0.698381  0.936899  0.101806

In [45]: df.loc[:, ["a", "b", "c"]] ## label based selective column slicing 
Out[45]: 
          a         b         c
0  0.409038  0.745497  0.890767
1  0.570642  0.181552  0.794599
2  0.568440  0.501638  0.186635
3  0.679125  0.642817  0.697628

In [46]: df.loc[:, "a":"c"] ## label based column ranges slicing 
Out[46]: 
          a         b         c
0  0.409038  0.745497  0.890767
1  0.570642  0.181552  0.794599
2  0.568440  0.501638  0.186635
3  0.679125  0.642817  0.697628

In [47]: df.iloc[:, 0:3] ## index based column ranges slicing 
Out[47]: 
          a         b         c
0  0.409038  0.745497  0.890767
1  0.570642  0.181552  0.794599
2  0.568440  0.501638  0.186635
3  0.679125  0.642817  0.697628

### with 2 different column ranges, index based slicing: 
In [49]: df[df.columns[0:1].tolist() + df.columns[1:3].tolist()]
Out[49]: 
          a         b         c
0  0.409038  0.745497  0.890767
1  0.570642  0.181552  0.794599
2  0.568440  0.501638  0.186635
3  0.679125  0.642817  0.697628
Surya
fonte
Tente evitar apenas descartar o código como resposta e tente explicar o que ele faz e por quê. Seu código pode não ser óbvio para pessoas que não têm a experiência de codificação relevante. Edite sua resposta para incluir esclarecimentos, contexto e tente mencionar quaisquer limitações, suposições ou simplificações em sua resposta.
Sᴀᴍ Onᴇᴌᴀ
1

Seu equivalente

 >>> print(df2.loc[140:160,['Relevance','Title']])
 >>> print(df2.ix[140:160,[3,7]])
Max Kleiner
fonte
1

se o quadro de dados for assim:

group         name      count
fruit         apple     90
fruit         banana    150
fruit         orange    130
vegetable     broccoli  80
vegetable     kale      70
vegetable     lettuce   125

e OUTPUT pode ser como

   group    name  count
0  fruit   apple     90
1  fruit  banana    150
2  fruit  orange    130

se você usar o operador lógico np.logical_not

df[np.logical_not(df['group'] == 'vegetable')]

mais sobre

https://docs.scipy.org/doc/numpy-1.13.0/reference/routines.logic.html

outros operadores lógicos

  1. logic_and (x1, x2, / [, out, where, ...]) Calcule o valor de verdade de x1 AND x2 em termos de elementos.

  2. logic_or (x1, x2, / [, out, where, casting, ...]) Calcule o valor de verdade de x1 OR x2 em termos de elementos.

  3. logic_not (x, / [, out, where, casting, ...]) Calcule o valor de verdade de NOT x em termos de elementos.
  4. logic_xor (x1, x2, / [, fora, onde, ..]) Calcule o valor de verdade de x1 XOR x2, em elementos.
Vladimir Gavrysh
fonte
0

Outra maneira de obter um subconjunto de colunas da trama de dados, supondo que você quer todas as linhas, seria fazer:
data[['a','b']]e data[['c','d','e']]
Se você quiser usar índices de coluna numéricos que você pode fazer:
data[data.columns[:2]]edata[data.columns[2:]]

Camilo
fonte