pandas GroupNaN (eksik) değerlere sahip sütunlar


147

Ben groupby istediğim sütunlarda birçok eksik değerleri olan bir DataFrame var:

import pandas as pd
import numpy as np
df = pd.DataFrame({'a': ['1', '2', '3'], 'b': ['4', np.NaN, '6']})

In [4]: df.groupby('b').groups
Out[4]: {'4': [0], '6': [2]}

Pandaların NaN hedef değerleri olan satırları bıraktığını görün. (Bu satırları dahil etmek istiyorum!)

Bu tür birçok işleme ihtiyaç duyduğumdan (birçok cols eksik değerlere sahip) ve sadece medyanlardan (genellikle rastgele ormanlar) daha karmaşık işlevler kullandığım için, çok karmaşık kod parçaları yazmaktan kaçınmak istiyorum.

Baska öneri? Bunun için bir işlev yazmalı mıyım yoksa basit bir çözüm var mı?


1
@PhillipCloud Bu soruyu , Jeff'in açık panda geliştirmesiyle ilgili sadece oldukça iyi olan soruyu içerecek şekilde düzenledim .
Andy Hayden

1
NaN'leri gruplara dahil edememek (ve çoğaltmak) oldukça ağırlaştırıcıdır. R'den alıntı yapmak ikna edici değildir, çünkü bu davranış başka birçok şeyle tutarlı değildir. Her neyse, kukla kesmek de oldukça kötü. Bununla birlikte, bir grubun boyutu (NaN'leri içerir) ve sayısı (NaN'leri yoksayar), NaN'ler varsa farklılık gösterir. dfgrouped = df.groupby (['b']). a.agg (['sum', 'size', 'count']) dfgrouped ['sum'] [dfgrouped ['size']! = dfgrouped ['sayısı ']] = Yok
Brian Preslopsky

Özellikle neyi başarmaya çalıştığınızı özetleyebilir misiniz? yani bir çıktı görüyoruz, ama "istenen" çıktı nedir?
ca

2
Pandalar 1.1 ile yakında belirtmek mümkün olacak dropna=Falseiçinde groupby(), istenen sonucu elde etmek. Daha fazla bilgi
cs95

Yanıtlar:


130

Bu dokümanların Eksik Veriler bölümünde belirtilmiştir :

GroupBy'deki NA grupları otomatik olarak hariç tutulur. Örneğin, bu davranış R ile tutarlıdır.

Çözümlerden biri, groupby'yi (ör. -1) yapmadan önce bir yer tutucu kullanmaktır:

In [11]: df.fillna(-1)
Out[11]: 
   a   b
0  1   4
1  2  -1
2  3   6

In [12]: df.fillna(-1).groupby('b').sum()
Out[12]: 
    a
b    
-1  2
4   1
6   3

Bununla birlikte, bu oldukça korkunç bir saldırı hissi veriyor ... belki de NaN'yi groupby'ye dahil etme seçeneği olmalı ( aynı yer tutucu kesmek kullanan bu github sorununa bakın ).


4
Bu daha önce düşündüğüm mantıklı ama komik bir çözüm, Pandas NaN alanlarını boş alanlardan yapıyor ve onları geri değiştirmek zorundayız. Bu yüzden bir SQL sunucusu çalıştırmak ve oradan tabloları sorgulamak (biraz çok karmaşık görünüyor) veya Pandalar rağmen başka bir kütüphane arıyorum ya da kendi (kullanmak istediğim) kullanmak gibi diğer çözümler arıyor düşünüyorum düşünüyorum kurtulmak için). Thx
Gyula Sámuel Karli

@ GyulaSámuelKarli Bana göre bu küçük bir hata gibi görünüyor (yukarıdaki hata raporuna bakın) ve benim çözümüm geçici bir çözümdür. Tüm kütüphaneyi yazman garip geliyor.
Andy Hayden

1
Pandaları yazmak istemiyorum, sadece istekleri en çok karşılayan aracı arayın.
Gyula Sámuel Karli

1
Aşağıdaki cevabıma bir göz atın, oldukça iyi (daha temiz ve muhtemelen daha hızlı) bir çözüm bulduğuma inanıyorum. stackoverflow.com/a/43375020/408853
ca

4
Hayır, bu R ile tutarlı değildir. Df%>% group_by, gruplandırma sütununu fct_explicit_na'dan geçirerek önlenebilecek bir uyarı ile NA özetleri de verir ve ardından (Eksik) bir seviye oluşturulur.
Ravaging Care

40

Eski konu, eğer birisi hala bunun üzerinde tökezliyorsa - başka bir geçici çözüm, gruplamadan önce .astype (str) yoluyla dizeye dönüştürmektir. Bu, NaN'leri koruyacaktır.

in:
df = pd.DataFrame({'a': ['1', '2', '3'], 'b': ['4', np.NaN, '6']})
df['b'] = df['b'].astype(str)
df.groupby(['b']).sum()
out:
    a
b   
4   1
6   3
nan 2

@ K3 --- rnc: Bağlantınızın yorumuna bakın - bağlantınızdaki yayının yazarı yanlış bir şey yaptı.
Thomas

@Thomas, evet, yukarıdaki örnekte olduğu gibi. Örneği güvenli hale getirebilirseniz (ve önemsiz olarak) lütfen düzenleyin.
K3 - rnc

sumArasında aburada dize birleştirme değil, bir sayısal toplamıdır. Bu sadece "işe yarar" çünkü 'b' farklı girdilerden oluşuyordu. Sayısal olmak için 'a' ve dize olmak için 'b' gerekir
BallpointBen

28

pandalar> = 1.1

Pandalar 1.1'den bu davranış üzerinde daha iyi bir kontrole sahip olursunuz, NA değerlerine artık gruplayıcıda aşağıdakileri kullanarak izin verilirdropna=False :

pd.__version__
# '1.1.0.dev0+2004.g8d10bfb6f'

# Example from the docs
df

   a    b  c
0  1  2.0  3
1  1  NaN  4
2  2  1.0  3
3  1  2.0  2

# without NA (the default)
df.groupby('b').sum()

     a  c
b        
1.0  2  3
2.0  2  5
# with NA
df.groupby('b', dropna=False).sum()

     a  c
b        
1.0  2  3
2.0  2  5
NaN  1  4

4
Umarım bu cevap zirveye doğru kademeli bir yürüyüş yapar. Doğru yaklaşım.
kdbanman

1.1 henüz yayınlanmadığını düşünüyorum. Conda ve pip ve sürümleri üzerinde kontrol hala 1.0.4
sammywemmy

1
@sammywemmy Evet, şimdilik bu sadece bir geliştirme ortamında çalıştırılabilir . Eski SO yayınlarına yeni özellikler getirme konusunda bir başlangıç ​​yapmayı seviyorum. ;-)
cs95

9

Yeterli itibar puanım olmadığı için M. Kiewisch'e yorum ekleyemiyorum (sadece 41 var ama yorum yapmak için 50'den fazla gerekir).

Her neyse, sadece M. Kiewisch çözümünün olduğu gibi çalışmadığını ve daha fazla ayarlamaya ihtiyaç duyabileceğini belirtmek istiyorum. Örneğin düşünün

>>> df = pd.DataFrame({'a': [1, 2, 3, 5], 'b': [4, np.NaN, 6, 4]})
>>> df
   a    b
0  1  4.0
1  2  NaN
2  3  6.0
3  5  4.0
>>> df.groupby(['b']).sum()
     a
b
4.0  6
6.0  3
>>> df.astype(str).groupby(['b']).sum()
      a
b
4.0  15
6.0   3
nan   2

Bu, b = 4.0 grubu için karşılık gelen değerin 6 yerine 15 olduğunu gösterir. Burada 1 ve 5'i sayı olarak eklemek yerine dize olarak birleştiriyor.


12
Çünkü sadece bsütun yerine tüm
DF'yi str'ye

Bunun söz konusu cevapta düzeltildiğine dikkat edin.
Shaido - Monica'yı geri yükle

1
Yeni çözüm bence daha iyi ama yine de güvenli değil. 'B' sütunundaki girdilerden birinin dize np.NaN ile aynı olduğu bir durumu düşünün. Sonra bu şeyler bir araya toplanır. df = pd.DataFrame ({'a': [1, 2, 3, 5, 6], 'b': ['foo', np.NaN, 'bar', 'foo', 'nan']}) ; df ['b'] = df ['b']. astype (str); df.groupby (['b']). sum ()
Kamaraju Kusumanchi

6

Andy Hayden'ın çözümüne küçük bir nokta - işe yaramıyor (artık?) Çünkü np.nan == np.nanverim False, bu yüzden replaceişlev aslında hiçbir şey yapmıyor.

Benim için işe yarayan şuydu:

df['b'] = df['b'].apply(lambda x: x if not np.isnan(x) else -1)

(En azından Panda'nın davranışı 0.19.2. Farklı bir cevap olarak eklediğim için üzgünüm, yorum yapmak için yeterli itibarım yok.)


12
Ayrıca var df['b'].fillna(-1).
K3 - rnc

6

Şimdiye kadar verilen tüm cevaplar, aslında veri kümesinin bir parçası olan bir kukla değer seçmeniz mümkün olduğundan, potansiyel olarak tehlikeli davranışlara neden olur. Birçok özelliğe sahip gruplar oluştururken bu durum büyük olasılıkla artar. Basitçe söylemek gerekirse, yaklaşım her zaman iyi değildir.

Daha az hacky bir çözüm, her biri kendi kimliğine sahip benzersiz bir değer kombinasyonları dizini oluşturmak için pd.drop_duplicates () kullanmak ve daha sonra bu kimlik üzerinde gruplamaktır. Daha ayrıntılı ama işi bitiriyor:

def safe_groupby(df, group_cols, agg_dict):
    # set name of group col to unique value
    group_id = 'group_id'
    while group_id in df.columns:
        group_id += 'x'
    # get final order of columns
    agg_col_order = (group_cols + list(agg_dict.keys()))
    # create unique index of grouped values
    group_idx = df[group_cols].drop_duplicates()
    group_idx[group_id] = np.arange(group_idx.shape[0])
    # merge unique index on dataframe
    df = df.merge(group_idx, on=group_cols)
    # group dataframe on group id and aggregate values
    df_agg = df.groupby(group_id, as_index=True)\
               .agg(agg_dict)
    # merge grouped value index to results of aggregation
    df_agg = group_idx.set_index(group_id).join(df_agg)
    # rename index
    df_agg.index.name = None
    # return reordered columns
    return df_agg[agg_col_order]

Şimdi aşağıdakileri yapabileceğinizi unutmayın:

data_block = [np.tile([None, 'A'], 3),
              np.repeat(['B', 'C'], 3),
              [1] * (2 * 3)]

col_names = ['col_a', 'col_b', 'value']

test_df = pd.DataFrame(data_block, index=col_names).T

grouped_df = safe_groupby(test_df, ['col_a', 'col_b'],
                          OrderedDict([('value', 'sum')]))

Bu, sahte bir değer olarak yanlış olan gerçek verilerin üzerine yazmaktan endişe etmeden başarılı sonucu döndürür.


Bu genel durum için en iyi çözüm, ancak bunun yerine kullanabileceğim geçersiz bir dize / sayı bildiğim durumlarda, muhtemelen Andy Hayden'ın aşağıdaki cevabı ile gideceğim ... Umarım pandalar bu davranışı yakında düzeltir.
Sarah Messer

4

Bunu zaten cevapladım, ancak cevabın bir yoruma dönüştürülmesinin bir nedeni var. Bununla birlikte, bu en verimli çözümdür:

NaN'leri gruplara dahil edememek (ve çoğaltamamak) oldukça ağırlaştırıcıdır. R'den alıntı yapmak ikna edici değildir, çünkü bu davranış başka birçok şeyle tutarlı değildir. Her neyse, kukla kesmek de oldukça kötü. Bununla birlikte, bir grubun boyutu (NaN'leri içerir) ve sayısı (NaN'leri yoksayar), NaN'ler varsa farklılık gösterir.

dfgrouped = df.groupby(['b']).a.agg(['sum','size','count'])

dfgrouped['sum'][dfgrouped['size']!=dfgrouped['count']] = None

Bunlar farklı olduğunda, bu grubun toplama işlevinin sonucu için değeri Hiçbiri olarak ayarlayabilirsiniz.


1
Bu bana çok yardımcı oldu ama orijinalinden biraz farklı bir soruya cevap veriyor. IIUC, çözümünüz NaN'leri toplamda çoğaltır, ancak "b" sütunundaki NaN öğeleri hala satır olarak bırakılır.
Andrew

0

Anaconda'da Pandalar 1.1 yüklü

CS95'in cevabı hakkında yorum yapamıyorum ama sorunu çözmeme yardımcı oldu.

Pandalar 1.1'i yüklemeye çalıştım ama kodunu kullanarak başarısız oldu, bu yüzden googled ve yükleyebiliyorum.

Önce anaconda komut istemini yönetici olarak çalıştırıyorum ve aşağıdaki kodu yapıştırıyorum:

pip install pandas==1.1.0rc0

Bundan sonra kullanımı içerir dropna = False

Bağlantı: https://libraries.io/pypi/pandas


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.