KeyError veren len (dize) içeren koşullu bir ifadeye dayalı olarak panda DataFrame'deki satırları silme


303

Bir panda DataFrame var ve belirli bir sütundaki dize uzunluğunun 2'den büyük olduğu satırları silmek istiyorum.

Bunu ( bu cevap başına ) yapabilmek için bekliyoruz :

df[(len(df['column name']) < 2)]

ama sadece hatayı alıyorum:

KeyError: u'no item named False'

Neyi yanlış yapıyorum?

(Not: df.dropna()İçerdiği satırlardan kurtulmak için kullanabileceğimi biliyorum NaN, ancak koşullu ifadeye dayalı satırları nasıl kaldıracağımı görmedim.)

Yanıtlar:


169

Bunu yaptığınızda len(df['column name']), yalnızca bir sayı, yani DataFrame'deki satır sayısı (yani, sütunun kendisinin uzunluğu) elde edersiniz. lenSütundaki her bir öğeye uygulamak istiyorsanız, öğesini kullanın df['column name'].map(len). O zaman dene

df[df['column name'].map(len) < 2]

3
Bir liste kavrayışı kullanarak bir yol buldum: df[[(len(x) < 2) for x in df['column name']]]ama seninki çok daha hoş. Yardımınız için teşekkürler!
sjs

13
Birinin daha karmaşık bir karşılaştırmaya ihtiyacı olması durumunda, her zaman bir lambda kullanılabilir. df[df['column name'].map(lambda x: str(x)!=".")]
4berto

1
Bazı nedenlerden dolayı, @ 4lberto tarafından gönderilenler dışında, diğer seçeneklerin hiçbiri benim için çalışmadı. Ben pandas 0.23.4ve python 3.6
goelakash

1
Daha .copy()sonra bu veri çerçevesini düzenlemek istiyorsanız (örneğin, yeni sütunlar atamak "bir değer bir DataFrame bir dilim kopyası üzerinde ayarlanmaya çalışıyordu" uyarısı)
sonunda ekleyeceğim

808

Bu sorunun orijinal başlığını doğrudan bir koşullu ifadeye dayalı bir pandalar DataFrame'den nasıl silebilirim? damla yöntemi:

df = df.drop(some labels)

df = df.drop(df[<some boolean condition>].index)

Misal

'Puan' sütununun <50 olduğu tüm satırları kaldırmak için:

df = df.drop(df[df.score < 50].index)

Yerinde sürüm (yorumlarda belirtildiği gibi)

df.drop(df[df.score < 50].index, inplace=True)

Birden fazla koşul

(bkz. Boolean Dizine Ekleme )

İşleçler: |için or, &için andve ~içindir not. Bunlar parantez kullanılarak gruplandırılmalıdır.

'Skor' sütununun <50 ve> 20 olduğu tüm satırları kaldırmak için

df = df.drop(df[(df.score < 50) & (df.score > 20)].index)


32
Sadece damla fonksiyonunun yerinde değiştirmeyi desteklediğini belirtmek istiyorum. Yani ,. çözümünüz df.drop ile aynıdır (df [df.score <50] .index, inplace = True). Yine de "endeks" hile bilmiyordu. Bana çok yardımcı oldu
Quickbeam2k1

9
Sadece bu indeks numarasını kullanmadan önce indeks değerlerinizin benzersiz (veya çağrı reset_index()) olduğundan emin olmanız gerektiğini belirtmek isteriz . Veri çerçevemden birçok satıra bırakıldığında bunu zor yoldan öğrendim.
Jay

3
sütun türünün str olduğu tüm satırları nasıl bırakabilirim? Yalnızca liste sütun türlerini tutmak istiyorum. Denedim test = df.drop(df[df['col1'].dtype == str].index)ama ben KeyError: False de denedim hata alıyorum df.drop(df[df.col1.dtype == str].index)ve df.drop(df[type(df.cleaned_norm_email) == str].index)hiçbir şey işe yarıyor gibi görünüyor? Herkes tavsiye edebilir. Teşekkürler! @User
PyRsquared

1
Bu eski bir soru ama ... @ su ile mücadele edilen balık bundan çok daha hızlı. df[(df.score < 50) & (df.score > 20)]Cevabınızın bir parçası olarak hesapladığınızı unutmayın . Eğer bunu tersine çevirirseniz df = df[(df.score >= 50) | (df.score <= 20)]cevabınız çok daha hızlı olur.
Roobie Nuby

1
@RoobieNuby - aynı koşulda değiller.
Nguai al

106

DataFrameKendisini filtrelenmiş bir sürümüne atayabilirsiniz :

df = df[df.score > 50]

Bu daha hızlıdır drop:

%%timeit
test = pd.DataFrame({'x': np.random.randn(int(1e6))})
test = test[test.x < 0]
# 54.5 ms ± 2.02 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

%%timeit
test = pd.DataFrame({'x': np.random.randn(int(1e6))})
test.drop(test[test.x > 0].index, inplace=True)
# 201 ms ± 17.9 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

%%timeit
test = pd.DataFrame({'x': np.random.randn(int(1e6))})
test = test.drop(test[test.x > 0].index)
# 194 ms ± 7.03 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

Birden çok sütunu kullanarak veya koşulunu nasıl denetlerim?
Piyush S. Wanare


9

dropÜcretsiz bir alternatif sunmak için @ User'ın genel çözümünü genişleteceğim . Bu, sorunun başlığına (OP'nin sorunu değil) dayalı olarak yönlendirilen kişiler içindir.

Negatif değerlere sahip tüm satırları silmek istediğinizi varsayalım. Bir astar çözümü: -

df = df[(df > 0).all(axis=1)]

Adım adım Açıklama: -

5x5 rastgele normal dağılım veri çerçevesi oluşturalım

np.random.seed(0)
df = pd.DataFrame(np.random.randn(5,5), columns=list('ABCDE'))
      A         B         C         D         E
0  1.764052  0.400157  0.978738  2.240893  1.867558
1 -0.977278  0.950088 -0.151357 -0.103219  0.410599
2  0.144044  1.454274  0.761038  0.121675  0.443863
3  0.333674  1.494079 -0.205158  0.313068 -0.854096
4 -2.552990  0.653619  0.864436 -0.742165  2.269755

Durumun negatifleri silmesine izin verin. Durumu sağlayan bir boolean df: -

df > 0
      A     B      C      D      E
0   True  True   True   True   True
1  False  True  False  False   True
2   True  True   True   True   True
3   True  True  False   True  False
4  False  True   True  False   True

Koşulu karşılayan tüm satırlar için bir boole serisi Satırdaki herhangi bir öğe, satır yanlış olarak işaretlenmiş koşulda başarısız olursa, Not

(df > 0).all(axis=1)
0     True
1    False
2     True
3    False
4    False
dtype: bool

Son olarak koşullara göre veri çerçevesindeki satırları filtreleyin

df[(df > 0).all(axis=1)]
      A         B         C         D         E
0  1.764052  0.400157  0.978738  2.240893  1.867558
2  0.144044  1.454274  0.761038  0.121675  0.443863

Sen aslında hiç df için geri atayabilirsiniz silmek vs filtre üzerinde yapılan ing
df = df[(df > 0).all(axis=1)]

NaN içeren satırları filtrelemek için kolayca genişletilebilir (sayısal olmayan girişler): -
df = df[(~df.isnull()).all(axis=1)]

Bu, aşağıdaki gibi durumlar için de basitleştirilebilir: E sütununun negatif olduğu tüm satırları silin

df = df[(df.E>0)]

@ Kullanıcı dropçözüm neden ham sütun tabanlı filtrasyon daha yavaş bazı profilleme istatistikleri ile bitirmek istiyorum : -

%timeit df_new = df[(df.E>0)]
345 µs ± 10.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit dft.drop(dft[dft.E < 0].index, inplace=True)
890 µs ± 94.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

Bir sütun temelde olan Seriesdiğer bir deyişle NumPyherhangi bir maliyet olmadan endekslenebilir, dizi. Temel bellek organizasyonunun yürütme hızına nasıl girdiğiyle ilgilenen insanlar için, burada Pandaları Hızlandırma ile ilgili harika bir Bağlantı :


6

Pandalarda str.lensınırınızla ve filtrelemek için Boolean sonucunu kullanarak yapabilirsiniz .

df[df['column name'].str.len().lt(2)]

3

Veri çerçevesi satırlarını sütun değeri üzerinde bazı karmaşık koşullara göre bırakmak isterseniz, yukarıda gösterilen şekilde karmaşık olabilir. Her zaman çalışan aşağıdaki daha basit bir çözüme sahibim. Sütunu 'başlık' ile bırakmak istediğinizi varsayalım, bu yüzden önce bu sütunu bir listeye alın.

text_data = df['name'].tolist()

şimdi listenin her öğesine bir işlev uygulayın ve bunu bir panda serisine yerleştirin:

text_length = pd.Series([func(t) for t in text_data])

benim durumumda sadece jeton sayısını almaya çalışıyordum:

text_length = pd.Series([len(t.split()) for t in text_data])

şimdi veri çerçevesine yukarıdaki serilerle birlikte bir sütun daha ekleyin:

df = df.assign(text_length = text_length .values)

şimdi yeni sütuna aşağıdaki gibi koşul uygulayabiliriz:

df = df[df.text_length  >  10]
def pass_filter(df, label, length, pass_type):

    text_data = df[label].tolist()

    text_length = pd.Series([len(t.split()) for t in text_data])

    df = df.assign(text_length = text_length .values)

    if pass_type == 'high':
        df = df[df.text_length  >  length]

    if pass_type == 'low':
        df = df[df.text_length  <  length]

    df = df.drop(columns=['text_length'])

    return df
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.