Pandaları veri çerçevesi dize girişini ayrı satırlara bölme (patlatma)


203

Bir sahip pandas dataframeolan metin dizgilerinin bir sütun virgülle ayrılmış değer içerir. Her CSV alanını bölmek ve giriş başına yeni bir satır oluşturmak istiyorum (CSV'nin temiz olduğunu ve yalnızca ',' üzerinde bölünmesi gerektiğini varsayın). Örneğin, ahaline gelmelidir b:

In [7]: a
Out[7]: 
    var1  var2
0  a,b,c     1
1  d,e,f     2

In [8]: b
Out[8]: 
  var1  var2
0    a     1
1    b     1
2    c     1
3    d     2
4    e     2
5    f     2

Şimdiye kadar, çeşitli basit işlevleri denedim, ancak .applyyöntem bir eksende kullanıldığında yalnızca bir satırı dönüş değeri olarak kabul ediyor ve .transformişe yarayamıyorum. Herhangi bir öneri çok takdir edilecektir!

Örnek veriler:

from pandas import DataFrame
import numpy as np
a = DataFrame([{'var1': 'a,b,c', 'var2': 1},
               {'var1': 'd,e,f', 'var2': 2}])
b = DataFrame([{'var1': 'a', 'var2': 1},
               {'var1': 'b', 'var2': 1},
               {'var1': 'c', 'var2': 1},
               {'var1': 'd', 'var2': 2},
               {'var1': 'e', 'var2': 2},
               {'var1': 'f', 'var2': 2}])

Bu işe yaramaz biliyorum çünkü biz numpy geçerek DataFrame meta-veri kaybetmek, ama size ne yapmaya çalıştım bir fikir vermelidir:

def fun(row):
    letters = row['var1']
    letters = letters.split(',')
    out = np.array([row] * len(letters))
    out['var1'] = letters
a['idx'] = range(a.shape[0])
z = a.groupby('idx')
z.transform(fun)

2
Bu sayfadaki diğer çözümler çalışıyor ancak kısa ve etkili bir çözüm buldum. stackoverflow.com/questions/27263805/…
desaiankitb

1
Bu sayfaya gelen ve birden çok sütun tutan bir çözüm arayan diğerleri için şu soruya göz atın: stackoverflow.com/questions/17116814/…
Sos

Yanıtlar:


81

Böyle bir şeye ne dersin:

In [55]: pd.concat([Series(row['var2'], row['var1'].split(','))              
                    for _, row in a.iterrows()]).reset_index()
Out[55]: 
  index  0
0     a  1
1     b  1
2     c  1
3     d  2
4     e  2
5     f  2

Sonra sadece sütunları yeniden adlandırmanız gerekir


1
Bu işe yarayacak gibi görünüyor. Yardımınız için teşekkürler! Genel olarak, Split'in Apply (Uygula) rasgele büyüklüğünde bir veri çerçevesi döndürdüğü (ancak tüm parçalar için tutarlı olan) ve Combine döndürülen DF'leri vstacks ettiğinde Split-Apply-Combine için tercih edilen bir yaklaşım var mı?
Vincent

GroupBy.apply çalışması gerekir (Ben sadece master karşı denedim). Ancak, bu durumda verileri satır satır oluşturduğunuz için fazladan gruplama adımından geçmeniz gerekmez mi?
Chang She

1
Hey Millet. Bu kadar geç atladığım için üzgünüm ama daha iyi bir çözüm olup olmadığını merak ediyorum. Bunun için bilet gibi göründüğü için ilk kez iterrows ile denemeye çalışıyorum. Ayrıca önerilen çözümle kafam karıştı. "_" Neyi temsil eder? Çözümün nasıl çalıştığını açıklayabilir misiniz? --Teşekkür ederiz
horatio1701d

11
Çözüm ikiden fazla sütuna genişletilebilir mi?
horatio1701d

1
lütfen bu vektörize yaklaşımı kontrol edin ...
MaxU

147

GÜNCELLEME2: birden çok normalve birden çok listsütun için çalışacak daha genel vektörize fonksiyon

def explode(df, lst_cols, fill_value='', preserve_index=False):
    # make sure `lst_cols` is list-alike
    if (lst_cols is not None
        and len(lst_cols) > 0
        and not isinstance(lst_cols, (list, tuple, np.ndarray, pd.Series))):
        lst_cols = [lst_cols]
    # all columns except `lst_cols`
    idx_cols = df.columns.difference(lst_cols)
    # calculate lengths of lists
    lens = df[lst_cols[0]].str.len()
    # preserve original index values    
    idx = np.repeat(df.index.values, lens)
    # create "exploded" DF
    res = (pd.DataFrame({
                col:np.repeat(df[col].values, lens)
                for col in idx_cols},
                index=idx)
             .assign(**{col:np.concatenate(df.loc[lens>0, col].values)
                            for col in lst_cols}))
    # append those rows that have empty lists
    if (lens == 0).any():
        # at least one list in cells is empty
        res = (res.append(df.loc[lens==0, idx_cols], sort=False)
                  .fillna(fill_value))
    # revert the original index order
    res = res.sort_index()
    # reset index if requested
    if not preserve_index:        
        res = res.reset_index(drop=True)
    return res

Demo:

Birden çok listsütun - tüm listsütunların her satırda aynı öğe sayısına sahip olması gerekir:

In [134]: df
Out[134]:
   aaa  myid        num          text
0   10     1  [1, 2, 3]  [aa, bb, cc]
1   11     2         []            []
2   12     3     [1, 2]      [cc, dd]
3   13     4         []            []

In [135]: explode(df, ['num','text'], fill_value='')
Out[135]:
   aaa  myid num text
0   10     1   1   aa
1   10     1   2   bb
2   10     1   3   cc
3   11     2
4   12     3   1   cc
5   12     3   2   dd
6   13     4

orijinal dizin değerlerini koruyarak:

In [136]: explode(df, ['num','text'], fill_value='', preserve_index=True)
Out[136]:
   aaa  myid num text
0   10     1   1   aa
0   10     1   2   bb
0   10     1   3   cc
1   11     2
2   12     3   1   cc
2   12     3   2   dd
3   13     4

Kurmak:

df = pd.DataFrame({
 'aaa': {0: 10, 1: 11, 2: 12, 3: 13},
 'myid': {0: 1, 1: 2, 2: 3, 3: 4},
 'num': {0: [1, 2, 3], 1: [], 2: [1, 2], 3: []},
 'text': {0: ['aa', 'bb', 'cc'], 1: [], 2: ['cc', 'dd'], 3: []}
})

CSV sütunu:

In [46]: df
Out[46]:
        var1  var2 var3
0      a,b,c     1   XX
1  d,e,f,x,y     2   ZZ

In [47]: explode(df.assign(var1=df.var1.str.split(',')), 'var1')
Out[47]:
  var1  var2 var3
0    a     1   XX
1    b     1   XX
2    c     1   XX
3    d     2   ZZ
4    e     2   ZZ
5    f     2   ZZ
6    x     2   ZZ
7    y     2   ZZ

bu küçük hileyi kullanarak CSV benzeri sütunu sütuna dönüştürebiliriz list:

In [48]: df.assign(var1=df.var1.str.split(','))
Out[48]:
              var1  var2 var3
0        [a, b, c]     1   XX
1  [d, e, f, x, y]     2   ZZ

GÜNCELLEME: genel vektörize yaklaşım (birden çok sütun için de çalışır):

Orijinal DF:

In [177]: df
Out[177]:
        var1  var2 var3
0      a,b,c     1   XX
1  d,e,f,x,y     2   ZZ

Çözüm:

önce CSV dizelerini listelere dönüştürelim:

In [178]: lst_col = 'var1' 

In [179]: x = df.assign(**{lst_col:df[lst_col].str.split(',')})

In [180]: x
Out[180]:
              var1  var2 var3
0        [a, b, c]     1   XX
1  [d, e, f, x, y]     2   ZZ

Şimdi bunu yapabiliriz:

In [181]: pd.DataFrame({
     ...:     col:np.repeat(x[col].values, x[lst_col].str.len())
     ...:     for col in x.columns.difference([lst_col])
     ...: }).assign(**{lst_col:np.concatenate(x[lst_col].values)})[x.columns.tolist()]
     ...:
Out[181]:
  var1  var2 var3
0    a     1   XX
1    b     1   XX
2    c     1   XX
3    d     2   ZZ
4    e     2   ZZ
5    f     2   ZZ
6    x     2   ZZ
7    y     2   ZZ

ESKİ cevap:

@AFinkelstein çözümünden esinlenerek, DF'ye iki sütundan fazla ve AFinkelstein'ın çözümü kadar hızlı, neredeyse hızlı olarak uygulanabilecek biraz daha genelleştirmek istedim):

In [2]: df = pd.DataFrame(
   ...:    [{'var1': 'a,b,c', 'var2': 1, 'var3': 'XX'},
   ...:     {'var1': 'd,e,f,x,y', 'var2': 2, 'var3': 'ZZ'}]
   ...: )

In [3]: df
Out[3]:
        var1  var2 var3
0      a,b,c     1   XX
1  d,e,f,x,y     2   ZZ

In [4]: (df.set_index(df.columns.drop('var1',1).tolist())
   ...:    .var1.str.split(',', expand=True)
   ...:    .stack()
   ...:    .reset_index()
   ...:    .rename(columns={0:'var1'})
   ...:    .loc[:, df.columns]
   ...: )
Out[4]:
  var1  var2 var3
0    a     1   XX
1    b     1   XX
2    c     1   XX
3    d     2   ZZ
4    e     2   ZZ
5    f     2   ZZ
6    x     2   ZZ
7    y     2   ZZ

7
ahbap, Git pandalarında bir tartışma açabilirsen, bence böyle bir fonksiyona ihtiyacımız var !!! Pandalar için SO'da unlistify ve unnesting hakkında birçok soru gördüm
YOBEN_S

bunu birden çok sütun için kullanma. 2 sütunda virgülle ayrılmış veri varsa ve bunu sırayla yapmak istiyorum gibi?
Jaskaran Singh Puri

@JaskaranSinghPuri, önce tüm CSV sütunlarını listelere dönüştürmek istiyorsunuz.
MaxU

1
Ne yazık ki, liste öğeleriniz tuples ise işe yaramaz. Ama tüm dizeyi dizeye dönüştürdükten sonra, bir cazibe gibi çalışır!
Guido

2
WenBen'in iddiaları panda tanrıları tarafından duyulduğu anlaşılıyor .explode(), API'ye bir yöntem yüklediler (ayrıca bu cevaba bakınız ).
cs95

118

Kabul edilen cevaptan daha hızlı bir şey bulmak için acı dolu deneylerden sonra, bunu işe aldım. Denediğim veri kümesinde yaklaşık 100 kat daha hızlı koştu.

Birisi bunu daha zarif hale getirmenin bir yolunu biliyorsa, lütfen kodumu değiştirin. İndeks olarak tutmak istediğiniz diğer sütunları ayarlamadan ve ardından dizini sıfırlayıp sütunları yeniden adlandırmadan çalışan bir yol bulamadım, ancak çalışan başka bir şey olduğunu hayal ediyorum.

b = DataFrame(a.var1.str.split(',').tolist(), index=a.var2).stack()
b = b.reset_index()[[0, 'var2']] # var1 variable is currently labeled 0
b.columns = ['var1', 'var2'] # renaming var1

2
Bu çözüm çok daha hızlı çalıştı ve daha az bellek kullanıyor gibi görünüyor,
cyril

1
Bu güzel bir vectorized pandalar çözümü, onu arıyordum. Teşekkürler!
Dennis Golomazov

Bunu kendi veri TypeError: object of type 'float' has no len()DataFrame(df.var1.str.split(',').tolist())
kümemde denediğimde,

@ user5359531 veri kümenizin muhtemelen NaNbu sütunda bir kısmı vardır, bu nedenle değiştirmeb = DataFrame(a.var1.str.split(',').values.tolist(), index=a.var2).stack()
Flair

Sadece Bilginize burada örnekle bu çözümün kadar güzel yazma.
hhbilly

48

İşte bu ortak görev için yazdığım bir işlev . Series/ stackYöntemlerinden daha etkilidir . Sütun sırası ve adları korunur.

def tidy_split(df, column, sep='|', keep=False):
    """
    Split the values of a column and expand so the new DataFrame has one split
    value per row. Filters rows where the column is missing.

    Params
    ------
    df : pandas.DataFrame
        dataframe with the column to split and expand
    column : str
        the column to split and expand
    sep : str
        the string used to split the column's values
    keep : bool
        whether to retain the presplit value as it's own row

    Returns
    -------
    pandas.DataFrame
        Returns a dataframe with the same columns as `df`.
    """
    indexes = list()
    new_values = list()
    df = df.dropna(subset=[column])
    for i, presplit in enumerate(df[column].astype(str)):
        values = presplit.split(sep)
        if keep and len(values) > 1:
            indexes.append(i)
            new_values.append(presplit)
        for value in values:
            indexes.append(i)
            new_values.append(value)
    new_df = df.iloc[indexes, :].copy()
    new_df[column] = new_values
    return new_df

Bu işlevle, orijinal soru aşağıdaki kadar basittir:

tidy_split(a, 'var1', sep=',')

1
Bu çok hızlı! Bunun için çok teşekkürler.
Anurag N. Sharma

44

Pandalar> = 0.25

Seri ve DataFrame yöntemleri listeleri ayrı satırlara ayıran bir .explode()yöntem tanımlar . Liste benzeri bir sütunun patlaması ile ilgili dokümanlar bölümüne bakın .

Virgülle ayrılmış dizelerin bir listeniz olduğundan, öğelerin listesini almak için dizeyi virgülle bölün ve ardından explodebu sütunu arayın .

df = pd.DataFrame({'var1': ['a,b,c', 'd,e,f'], 'var2': [1, 2]})
df
    var1  var2
0  a,b,c     1
1  d,e,f     2

df.assign(var1=df['var1'].str.split(',')).explode('var1')

  var1  var2
0    a     1
0    b     1
0    c     1
1    d     2
1    e     2
1    f     2

explodeYalnızca tek bir sütunda (şimdilik) çalıştığını unutmayın .


NaN'ler ve boş listeler, doğru yapmak için çemberlerden atlamak zorunda kalmadan hak ettikleri tedaviyi alır.

df = pd.DataFrame({'var1': ['d,e,f', '', np.nan], 'var2': [1, 2, 3]})
df
    var1  var2
0  d,e,f     1
1            2
2    NaN     3

df['var1'].str.split(',')

0    [d, e, f]
1           []
2          NaN

df.assign(var1=df['var1'].str.split(',')).explode('var1')

  var1  var2
0    d     1
0    e     1
0    f     1
1          2  # empty list entry becomes empty string after exploding 
2  NaN     3  # NaN left un-touched

Bu, ravel+ repeattabanlı çözümlere (boş listeleri tamamen görmezden gelen ve NaN'lere boğulan) karşı ciddi bir avantajdır .


4
Bu en kolay ve benim durumuma en uygun! Teşekkürler!
Isaac Sim

14

Benzer soru: pandalar: Bir sütundaki metni birden çok satıra nasıl bölerim?

Şunları yapabilirsiniz:

>> a=pd.DataFrame({"var1":"a,b,c d,e,f".split(),"var2":[1,2]})
>> s = a.var1.str.split(",").apply(pd.Series, 1).stack()
>> s.index = s.index.droplevel(-1)
>> del a['var1']
>> a.join(s)
   var2 var1
0     1    a
0     1    b
0     1    c
1     2    d
1     2    e
1     2    f

2
Bir yeniden adlandırma kodu ekledikten sonra çalışır s.name = 'var1'
Jesse

14

TL; DR

import pandas as pd
import numpy as np

def explode_str(df, col, sep):
    s = df[col]
    i = np.arange(len(s)).repeat(s.str.count(sep) + 1)
    return df.iloc[i].assign(**{col: sep.join(s).split(sep)})

def explode_list(df, col):
    s = df[col]
    i = np.arange(len(s)).repeat(s.str.len())
    return df.iloc[i].assign(**{col: np.concatenate(s)})

gösteri

explode_str(a, 'var1', ',')

  var1  var2
0    a     1
0    b     1
0    c     1
1    d     2
1    e     2
1    f     2

Listeleri dolan yeni bir veri çerçevesi oluşturalım

d = a.assign(var1=lambda d: d.var1.str.split(','))

explode_list(d, 'var1')

  var1  var2
0    a     1
0    b     1
0    c     1
1    d     2
1    e     2
1    f     2

Genel yorumlar

Ben kullanacağız np.arangeile repeatBirlikte kullanabileceğiniz dataframe endeks pozisyonları üretmeye iloc.

SSS

Neden kullanmıyorum loc?

Dizin benzersiz olmayabilir ve kullanma locsorgulanan bir dizinle eşleşen her satırı döndürür.

Neden bu valuesözelliği kullanmıyorsunuz ve dilimlemiyorsunuz?

Arama yaparken values, veri çerçevesinin tamamı tek bir uyumlu "blokta" ise, Pandalar "blok" olan dizinin bir görünümünü döndürür. Aksi takdirde Pandalar yeni bir diziyi bir araya getirmelidir. Arnavut kaldırırken, bu dizi tek tip bir türden olmalıdır. Bu genellikle dtype ile bir dizi döndürmek anlamına gelir object. Özelliği ilocdilimlemek yerine kullanarak valueskendimi bununla uğraşmaktan kurtarıyorum.

Neden kullanıyorsun assign?

Ben kullandığınızda assignben patlayacağım aynı sütun adını kullanarak, ben mevcut sütun üzerine ve dataframe konumunu korumaktadır.

Endeks değerleri neden tekrarlanıyor?

ilocTekrarlanan pozisyonlarda kullanılması sayesinde , ortaya çıkan endeks aynı tekrarlanan paterni gösterir. Liste veya dize her öğe için bir tekrar.
Bu ile sıfırlanabilirreset_index(drop=True)


Dizeler için

Dizeleri erken ayırmak istemiyorum. Bu nedenle, bunun yerine sep, bölünecek olursam, ortaya çıkan listenin uzunluğunun ayırıcı sayısından bir fazla olacağı varsayılarak argümanın oluşumlarını sayıyorum .

Sonra kullanan sepiçin joindaha sonra dizeleri split.

def explode_str(df, col, sep):
    s = df[col]
    i = np.arange(len(s)).repeat(s.str.count(sep) + 1)
    return df.iloc[i].assign(**{col: sep.join(s).split(sep)})

Listeler için

sepZaten bölünmüş olduğu için oluşumları saymaya gerek yok hariç dizeler için benzer .

concatenateListeleri bir araya getirmek için Numpy's kullanıyorum .

import pandas as pd
import numpy as np

def explode_list(df, col):
    s = df[col]
    i = np.arange(len(s)).repeat(s.str.len())
    return df.iloc[i].assign(**{col: np.concatenate(s)})


Bunu beğendim. Gerçekten özlü ve performans da gerçekten iyi olmalı. Yine de bir soru: df.iloc [i] veri çerçevesinin yinelenen satırlarıyla aynı mı yoksa bundan daha mı verimli? Teşekkürler!
Tim

7

Veri çerçevesinin yapısını değiştirmeden veri çerçevesini bölme ve patlatma olasılığı vardır

Belirli sütunların verilerini bölme ve genişletme

Giriş:

    var1    var2
0   a,b,c   1
1   d,e,f   2



#Get the indexes which are repetative with the split 
temp = df['var1'].str.split(',')
df = df.reindex(df.index.repeat(temp.apply(len)))


df['var1'] = np.hstack(temp)

Dışarı:

    var1    var2
0   a   1
0   b   1
0   c   1
1   d   2
1   e   2
1   f   2

Düzenleme-1

Birden çok sütun için satırları bölme ve genişletme

Filename    RGB                                             RGB_type
0   A   [[0, 1650, 6, 39], [0, 1691, 1, 59], [50, 1402...   [r, g, b]
1   B   [[0, 1423, 16, 38], [0, 1445, 16, 46], [0, 141...   [r, g, b]

Referans sütuna göre dizin oluşturma ve sütun değeri bilgisini yığınla hizalama

df = df.reindex(df.index.repeat(df['RGB_type'].apply(len)))
df = df.groupby('Filename').apply(lambda x:x.apply(lambda y: pd.Series(y.iloc[0])))
df.reset_index(drop=True).ffill()

Dışarı:

                Filename    RGB_type    Top 1 colour    Top 1 frequency Top 2 colour    Top 2 frequency
    Filename                            
 A  0       A   r   0   1650    6   39
    1       A   g   0   1691    1   59
    2       A   b   50  1402    49  187
 B  0       B   r   0   1423    16  38
    1       B   g   0   1445    16  46
    2       B   b   0   1419    16  39

5

Rasgele sayı sütunları olan veri çerçeveleri için bir çözüm buldum (bir seferde yalnızca bir sütunun girişlerini ayırırken).

def splitDataFrameList(df,target_column,separator):
    ''' df = dataframe to split,
    target_column = the column containing the values to split
    separator = the symbol used to perform the split

    returns: a dataframe with each entry for the target column separated, with each element moved into a new row. 
    The values in the other columns are duplicated across the newly divided rows.
    '''
    def splitListToRows(row,row_accumulator,target_column,separator):
        split_row = row[target_column].split(separator)
        for s in split_row:
            new_row = row.to_dict()
            new_row[target_column] = s
            row_accumulator.append(new_row)
    new_rows = []
    df.apply(splitListToRows,axis=1,args = (new_rows,target_column,separator))
    new_df = pandas.DataFrame(new_rows)
    return new_df

2
Bu todict () dönüşümü nedeniyle güzel ama ne yazık ki yavaş:
MAQ

4

İşte splitpanda strerişimci yöntemini kullanan ve sonra her satırı tek bir diziye düzleştirmek için NumPy kullanan oldukça basit bir mesaj .

Karşılık gelen değerler, bölünmemiş sütunu doğru sayıda ile tekrarlayarak alınır np.repeat.

var1 = df.var1.str.split(',', expand=True).values.ravel()
var2 = np.repeat(df.var2.values, len(var1) / len(df))

pd.DataFrame({'var1': var1,
              'var2': var2})

  var1  var2
0    a     1
1    b     1
2    c     1
3    d     2
4    e     2
5    f     2

1
Bu çok güzel bir cevap olabilir. Ne yazık ki, çok sayıda sütun için ölçeklenmiyor, değil mi?
Michael Dorner

3

Listelerimi patlatmanın çeşitli yollarını kullanarak bellek yetersiz deneyimiyle mücadele ediyorum, bu yüzden hangi cevapların oylanacağına karar vermeme yardımcı olacak bazı kriterler hazırladım. Liste uzunluğunun liste sayısına göre değişen oranlarıyla beş senaryoyu test ettim. Aşağıdaki sonuçları paylaşma:

Zaman: (daha az daha iyi, büyük versiyonu görmek için tıklayın)

hız

Maksimum bellek kullanımı: (daha az daha iyidir)

En yüksek bellek kullanımı

Sonuç :

  • @ MaxU'nun cevabı (güncelleme 2), kod adı concatenate , neredeyse her durumda en iyi hızı sunarken, peek bellek kullanımını düşük tutar,
  • bkz DMulligan cevabı @ (kod adı yığını , nispeten küçük listeleri ile satır işlem sürü gerekir ve artan pik hafızasını gelemez)
  • kabul edilen @ Chang'ın yanıtı , birkaç satırı olan ancak çok büyük listeleri olan veri çerçeveleri için iyi çalışır.

Tüm ayrıntılar (işlevler ve karşılaştırma kodu) bu GitHub özetine aittir . Kıyaslama sorununun basitleştirildiğini ve dizelerin listeye bölünmesini içermediğini unutmayın - çoğu çözüm benzer bir şekilde gerçekleştirilmiştir.


Güzel karşılaştırma! Karşılaştırma ölçütlerini çizmek için kullandığınız bir kod yayınlamak ister misiniz?
MaxU

1
Lütfen bu bağlantıya bakın: gist.github.com/krassowski/0259a2cd2ba774ccd9f69bbcc3187fbf ( cevapta zaten var) - IMO hepsini buraya yapıştırmak biraz uzun sürecek.
krassowski

2

Mükemmel @ DMulligan'ın çözümüne dayanarak , bir veri çerçevesinin bir sütununu birden çok satıra ayıran ve orijinal veri çerçevesine geri birleştiren genel bir vektörize edilmiş (döngüsüz) işlev. Ayrıca change_column_orderbu cevaptan harika bir genel işlev kullanır .

def change_column_order(df, col_name, index):
    cols = df.columns.tolist()
    cols.remove(col_name)
    cols.insert(index, col_name)
    return df[cols]

def split_df(dataframe, col_name, sep):
    orig_col_index = dataframe.columns.tolist().index(col_name)
    orig_index_name = dataframe.index.name
    orig_columns = dataframe.columns
    dataframe = dataframe.reset_index()  # we need a natural 0-based index for proper merge
    index_col_name = (set(dataframe.columns) - set(orig_columns)).pop()
    df_split = pd.DataFrame(
        pd.DataFrame(dataframe[col_name].str.split(sep).tolist())
        .stack().reset_index(level=1, drop=1), columns=[col_name])
    df = dataframe.drop(col_name, axis=1)
    df = pd.merge(df, df_split, left_index=True, right_index=True, how='inner')
    df = df.set_index(index_col_name)
    df.index.name = orig_index_name
    # merge adds the column to the last place, so we need to move it back
    return change_column_order(df, col_name, orig_col_index)

Misal:

df = pd.DataFrame([['a:b', 1, 4], ['c:d', 2, 5], ['e:f:g:h', 3, 6]], 
                  columns=['Name', 'A', 'B'], index=[10, 12, 13])
df
        Name    A   B
    10   a:b     1   4
    12   c:d     2   5
    13   e:f:g:h 3   6

split_df(df, 'Name', ':')
    Name    A   B
10   a       1   4
10   b       1   4
12   c       2   5
12   d       2   5
13   e       3   6
13   f       3   6    
13   g       3   6    
13   h       3   6    

Sütunların orijinal dizinini ve sırasını koruduğunu unutmayın. Ayrıca sıralı olmayan dizine sahip veri çerçeveleri ile çalışır.


2
Bu benim için bu bir çatlak, güzel iş: stackoverflow.com/a/48554655/6672746
Evan

2

String işlevi split, bir seçenek olan 'genişletme' boole bağımsız değişkenini alabilir.

İşte bu argümanı kullanan bir çözüm:

(a.var1
  .str.split(",",expand=True)
  .set_index(a.var2)
  .stack()
  .reset_index(level=1, drop=True)
  .reset_index()
  .rename(columns={0:"var1"}))

1

Sadece jiln'in mükemmel cevabını yukarıdan kullandım, ancak birden fazla sütunu bölmek için genişletmek gerekiyordu. Paylaşacağımı düşündüm.

def splitDataFrameList(df,target_column,separator):
''' df = dataframe to split,
target_column = the column containing the values to split
separator = the symbol used to perform the split

returns: a dataframe with each entry for the target column separated, with each element moved into a new row. 
The values in the other columns are duplicated across the newly divided rows.
'''
def splitListToRows(row, row_accumulator, target_columns, separator):
    split_rows = []
    for target_column in target_columns:
        split_rows.append(row[target_column].split(separator))
    # Seperate for multiple columns
    for i in range(len(split_rows[0])):
        new_row = row.to_dict()
        for j in range(len(split_rows)):
            new_row[target_columns[j]] = split_rows[j][i]
        row_accumulator.append(new_row)
new_rows = []
df.apply(splitListToRows,axis=1,args = (new_rows,target_column,separator))
new_df = pd.DataFrame(new_rows)
return new_df

1

MultiUndex desteği ile MaxU'nun cevabını yükseltti

def explode(df, lst_cols, fill_value='', preserve_index=False):
    """
    usage:
        In [134]: df
        Out[134]:
           aaa  myid        num          text
        0   10     1  [1, 2, 3]  [aa, bb, cc]
        1   11     2         []            []
        2   12     3     [1, 2]      [cc, dd]
        3   13     4         []            []

        In [135]: explode(df, ['num','text'], fill_value='')
        Out[135]:
           aaa  myid num text
        0   10     1   1   aa
        1   10     1   2   bb
        2   10     1   3   cc
        3   11     2
        4   12     3   1   cc
        5   12     3   2   dd
        6   13     4
    """
    # make sure `lst_cols` is list-alike
    if (lst_cols is not None
        and len(lst_cols) > 0
        and not isinstance(lst_cols, (list, tuple, np.ndarray, pd.Series))):
        lst_cols = [lst_cols]
    # all columns except `lst_cols`
    idx_cols = df.columns.difference(lst_cols)
    # calculate lengths of lists
    lens = df[lst_cols[0]].str.len()
    # preserve original index values    
    idx = np.repeat(df.index.values, lens)
    res = (pd.DataFrame({
                col:np.repeat(df[col].values, lens)
                for col in idx_cols},
                index=idx)
             .assign(**{col:np.concatenate(df.loc[lens>0, col].values)
                            for col in lst_cols}))
    # append those rows that have empty lists
    if (lens == 0).any():
        # at least one list in cells is empty
        res = (res.append(df.loc[lens==0, idx_cols], sort=False)
                  .fillna(fill_value))
    # revert the original index order
    res = res.sort_index()
    # reset index if requested
    if not preserve_index:        
        res = res.reset_index(drop=True)

    # if original index is MultiIndex build the dataframe from the multiindex
    # create "exploded" DF
    if isinstance(df.index, pd.MultiIndex):
        res = res.reindex(
            index=pd.MultiIndex.from_tuples(
                res.index,
                names=['number', 'color']
            )
    )
    return res

1

Tek astar split(___, expand=True)ve levelve nameargümanlarını kullanarak reset_index():

>>> b = a.var1.str.split(',', expand=True).set_index(a.var2).stack().reset_index(level=0, name='var1')
>>> b
   var2 var1
0     1    a
1     1    b
2     1    c
0     2    d
1     2    e
2     2    f

bSorudaki gibi görünmeniz gerekiyorsa , şunları da yapabilirsiniz:

>>> b = b.reset_index(drop=True)[['var1', 'var2']]
>>> b
  var1  var2
0    a     1
1    b     1
2    c     1
3    d     2
4    e     2
5    f     2

0

Bu soruna aşağıdaki çözümü buldum:

def iter_var1(d):
    for _, row in d.iterrows():
        for v in row["var1"].split(","):
            yield (v, row["var2"])

new_a = DataFrame.from_records([i for i in iter_var1(a)],
        columns=["var1", "var2"])

0

Python kopya paketini kullanan başka bir çözüm

import copy
new_observations = list()
def pandas_explode(df, column_to_explode):
    new_observations = list()
    for row in df.to_dict(orient='records'):
        explode_values = row[column_to_explode]
        del row[column_to_explode]
        if type(explode_values) is list or type(explode_values) is tuple:
            for explode_value in explode_values:
                new_observation = copy.deepcopy(row)
                new_observation[column_to_explode] = explode_value
                new_observations.append(new_observation) 
        else:
            new_observation = copy.deepcopy(row)
            new_observation[column_to_explode] = explode_values
            new_observations.append(new_observation) 
    return_df = pd.DataFrame(new_observations)
    return return_df

df = pandas_explode(df, column_name)

0

Burada birçok cevap var ama kimse pandalar patlayabilir fonksiyonundan bahsetmediğine şaşırdım. Aşağıdaki bağlantıya göz atın: https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.explode.html#pandas.DataFrame.explode

Bazı nedenlerden dolayı bu işleve erişemedim, bu yüzden aşağıdaki kodu kullandım:

import pandas_explode
pandas_explode.patch()
df_zlp_people_cnt3 = df_zlp_people_cnt2.explode('people')

resim açıklamasını buraya girin

Yukarıdaki verilerimin bir örneği. Gördüğünüz gibi insanlar sütununda bir dizi insan vardı ve ben de onu patlamaya çalışıyordum. Verdiğim kod liste türü verileri için çalışıyor. Bu yüzden virgülle ayrılmış metin verilerinizi liste biçimine getirmeye çalışın. Ayrıca kodum yerleşik işlevler kullandığından, özel / uygula işlevlerinden çok daha hızlıdır.

Not: pandas_explode'u pip ile kurmanız gerekebilir.


0

Benzer bir sorunum vardı, benim çözümüm veri çerçevesini ilk önce bir sözlük listesine dönüştürmek, sonra geçiş yapmaktı. İşte işlevi:

import copy
import re

def separate_row(df, column_name):
    ls = []
    for row_dict in df.to_dict('records'):
        for word in re.split(',', row_dict[column_name]):
            row = copy.deepcopy(row_dict)
            row[column_name]=word
            ls(row)
    return pd.DataFrame(ls)

Misal:

>>> from pandas import DataFrame
>>> import numpy as np
>>> a = DataFrame([{'var1': 'a,b,c', 'var2': 1},
               {'var1': 'd,e,f', 'var2': 2}])
>>> a
    var1  var2
0  a,b,c     1
1  d,e,f     2
>>> separate_row(a, "var1")
  var1  var2
0    a     1
1    b     1
2    c     1
3    d     2
4    e     2
5    f     2

Ayrıca liste türü satırlarını ayırmayı desteklemek için işlevi biraz değiştirebilirsiniz.

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.