Jak wziąć wycinki kolumn danych w pandach

264

Ładuję niektóre dane uczenia maszynowego z pliku CSV. Pierwsze 2 kolumny to obserwacje, a pozostałe kolumny to cechy.

Obecnie wykonuję następujące czynności:

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

co daje coś takiego:

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

Chciałbym ten dataframe pokroić w dwóch dataframes: jedna zawiera kolumny ai bjeden zawierający kolumny c, di e.

Nie można napisać czegoś takiego

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

Nie jestem pewien, jaka jest najlepsza metoda. Czy potrzebuję pd.Panel?

Nawiasem mówiąc, uważam, że indeksowanie ramek danych jest dość niespójne: data['a']jest dozwolone, ale data[0]nie jest. Z drugiej strony data['a':]nie jest dozwolone, ale data[0:]jest. Czy jest tego praktyczny powód? Jest to naprawdę mylące, jeśli kolumny są indeksowane przez Int, biorąc pod uwagę todata[0] != data[0:1]

CPA
źródło
3
DataFrame jest z natury obiektem przypominającym dykt, gdy robisz df [...], jednak df[5:10]dodano pewne udogodnienia, np. Dodano do wybierania wierszy ( pandas.pydata.org/pandas-docs/stable/… )
Wes McKinney
1
Czym więc ta niespójność jest decyzja projektowa na korzyść wygody? W porządku, ale zdecydowanie musi być bardziej wyraźny dla początkujących!
cpa
3
Uwzględnienie wygody projektowania sprawia, że ​​krzywa uczenia się jest bardzo stroma. Chciałbym, aby na początku była lepsza dokumentacja przedstawiająca tylko spójny interfejs. Na przykład, po prostu skup się na interfejsie IX.
Yu Shen

Odpowiedzi:

242

Odpowiedź 2017 - pandy 0.20: .ix jest przestarzałe. Użyj .loc

Zobacz wycofanie w dokumentach

.locużywa indeksowania opartego na etykietach, aby wybrać zarówno wiersze, jak i kolumny. Etykiety będące wartościami indeksu lub kolumn. Krojenie za pomocą .locobejmuje ostatni element.

Załóżmy, że mamy DataFrame z następującymi kolumnami:
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

.locakceptuje ten sam zapis wycinka, co listy Python dla wierszy i kolumn. Notacja plastra jeststart: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

Możesz pokroić według wierszy i kolumn. Na przykład, jeśli masz 5 wierszy z etykietami 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
źródło
jeśli twoje zastosowanie ma zastosowanie do wiersza lambda, jak w: df['newcol'] = df.apply(lambda row: myfunc(row), axis=1) wtedy możesz w myfunc(row){... użyć row['foo':'ant']. na przykład (zgodnie z odpowiedzią StackOverflow ), wewnątrz myfuncmożna dokonać oceny, jeśli którykolwiek z nich jest nienumeryczny:row['foo':'ant'].apply(lambda x: isinstance(x, str)).any()
pashute
4
.ilocnależy użyć teraz zamiast .loc. Napraw to, a ja to poprę.
ściągnął
1
@craned - to nie jest poprawne. Z dokumentacji Pandas: .loc opiera się głównie na etykietach, ale może być również używany z tablicą boolowską. .loc podniesie KeyError, gdy przedmioty nie zostaną znalezione. Podobna wypowiedź dotyczy .iloc, z tą różnicą, że odnosi się ona do krojenia opartego na indeksach. Innymi słowy w tym przykładzie użył indeksowania opartego na etykietach, a .loc jest właściwym wyborem (w zasadzie jedynym wyborem). Jeśli chcesz na przykład pokroić według pozycji - rzędy 5:10, użyj .iloc
user2103050
149

Uwaga: .ix jest przestarzałe od Pandas v0.20. Zamiast tego powinieneś użyć .loclub .iloc, odpowiednio.

Indeks DataFrame.ix jest tym, do którego chcesz uzyskać dostęp. Jest to trochę mylące (zgadzam się, że indeksowanie Pand jest czasem kłopotliwe!), Ale wydaje się, że następujące działania są takie, jak chcesz:

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

gdzie .ix [wycinek wiersza, wycinek kolumny] jest interpretowany. Więcej informacji na temat indeksowania pand tutaj: http://pandas.pydata.org/pandas-docs/stable/indexing.html#indexing-advanced

Karmel
źródło
5
Uważać, że zakresy w pand obejmują zarówno punktów końcowych, czyli>>>data.ix[:, 'a':'c'] a b c 0 0.859192 0.881433 0.843624 1 0.744979 0.427986 0.177159
polny
21
Można przejechać wiele kolumn w ten sposóbdf.ix[:,[0,3,4]]
użytkownik602599
3
@Karmel: Wygląda jak błąd kopiowania / wklejania w wynikach powyżej. Może miałeś na myśli df.ix[:,'b':'e']?
ChaimG,
6
Lepiej używać loczamiast ix: stackoverflow.com/a/31593712/4323
John Zwinck
5
Stare odpowiedzi tego typu muszą zostać usunięte. .ix jest przestarzałe i nigdy nie należy go używać.
Ted Petrou
75

Jako przykładu wykorzystajmy tytaniczny zestaw danych z pakietu dna morskiego

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

za pomocą nazw kolumn

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

za pomocą indeksów kolumn

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

przy użyciu ix (starsze niż Panda <wersja .20)

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

lub

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

przy użyciu metody reindex

>> titanic.reindex(columns=['sex','age','fare'])
Shankar ARUL - jupyterdata.com
źródło
6
W pandach 0.20: .ixjest przestarzałe.
Shihe Zhang,
ostrzeżenie o 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]
rezygnacji
35

Ponadto, biorąc pod uwagę DataFrame

dane

tak jak w twoim przykładzie, jeśli chcesz wyodrębnić tylko kolumnę aid (tj. pierwszą i czwartą kolumnę), iloc mothod z ramki danych pand jest tym, czego potrzebujesz i można go bardzo skutecznie wykorzystać. Wszystko, co musisz wiedzieć, to indeks kolumn, które chcesz wyodrębnić. Na przykład:

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

da tobie

          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
Mołdawia
źródło
25

Możesz pokroić wzdłuż kolumn a DataFrame, odwołując się do nazw każdej kolumny na liście, na przykład:

data = pandas.DataFrame(np.random.rand(10,5), columns = list('abcde'))
data_ab = data[list('ab')]
data_cde = data[list('cde')]
Brendan Wood
źródło
Więc jeśli chcę wszystkie dane zaczynające się od kolumny „b”, muszę znaleźć indeks „b” w data.columns i zrobić dane [data.columns [1:]]? To kanoniczny sposób działania?
cpa
1
Chcesz zaznaczyć wszystkie kolumny od „b”?
Brendan Wood,
Tak, lub zaznaczając wszystkie kolumny w danym zakresie.
cpa
Sam jestem całkiem nowy w pandach, więc nie mogę mówić o tym, co uważa się za kanoniczne. Zrobiłbym to tak, jak powiedziałeś, ale użyj get_locfunkcji on, data.columnsaby określić indeks kolumny „b” lub cokolwiek innego.
Brendan Wood,
20

A jeśli przyszedłeś tu po to, aby przeciąć dwa zakresy kolumn i połączyć je razem (jak ja), możesz zrobić coś takiego

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

Spowoduje to utworzenie nowej ramki danych z pierwszymi 900 kolumnami i (wszystkimi) kolumnami> 3593 (zakładając, że masz około 4000 kolumn w zestawie danych).

użytkownik2023507
źródło
Świetnie, ktoś to wypróbował ... Zastanawiałem się, czy to 0: 899, które dostaje pierwsze 900 kolumn .. dlaczego tak to zrobili? To wcale nie przypomina Pythona. Kiedy używasz zakresów w pythonie, zawsze jest to „aż do„ nie ”aż do i dołączone”
zwep
14

Oto, w jaki sposób można użyć różnych metod do selektywnego segmentowania kolumn, w tym selektywnego segmentowania opartego na etykietach, indeksowania i zakresów selektywnych.

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
źródło
Staraj się unikać po prostu odrzucania kodu jako odpowiedzi i spróbuj wyjaśnić, co robi i dlaczego. Twój kod może nie być oczywisty dla osób, które nie mają odpowiedniego doświadczenia w programowaniu. Edytuj swoją odpowiedź, aby uwzględnić wyjaśnienie, kontekst i spróbuj wspomnieć o wszelkich ograniczeniach, założeniach lub uproszczeniach w swojej odpowiedzi.
Sᴀᴍ Onᴇᴌᴀ
1

Jest to odpowiednik

 >>> print(df2.loc[140:160,['Relevance','Title']])
 >>> print(df2.ix[140:160,[3,7]])
Max Kleiner
źródło
1

jeśli ramka danych wygląda tak:

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

i OUTPUT może być jak

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

jeśli używasz operatora logicznego np.logical_not

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

Więcej na temat

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

inne operatory logiczne

  1. logical_and (x1, x2, / [, out, where, ...]) Oblicz wartość prawdy dla elementów x1 AND x2.

  2. logical_or (x1, x2, / [, out, where, casting, ...]) Oblicz wartość prawdy dla x1 LUB x2 w odniesieniu do elementu.

  3. logical_not (x, / [, out, where, casting, ...]) Oblicz wartość prawdy dla elementu NOT x.
  4. logical_xor (x1, x2, / [, out, where, ..]) Oblicz wartość prawdy x1 XOR x2, pod względem elementu.
Vladimir Gavrysh
źródło
0

Innym sposobem uzyskania podzbiór kolumn z DataFrame, zakładając, że chcesz wszystkie wiersze, byłoby to zrobić:
data[['a','b']]a data[['c','d','e']]
Jeśli chcesz użyć liczbowych indeksów kolumn można zrobić:
data[data.columns[:2]]idata[data.columns[2:]]

Camilo
źródło