स्प्लिट (एक्सप्लोड) पांडा डेटाफ्रेम स्ट्रिंग प्रविष्टि को अलग-अलग पंक्तियों के लिए


200

मेरे पास एक pandas dataframeपाठ स्ट्रिंग के एक कॉलम में अल्पविराम से अलग किए गए मान हैं। मैं प्रत्येक CSV फ़ील्ड को विभाजित करना चाहता हूं और प्रति प्रविष्टि एक नई पंक्ति बनाना चाहता हूं (मान लें कि CSV स्वच्छ हैं और केवल ',') पर विभाजित होने की आवश्यकता है। उदाहरण के लिए, aबनना चाहिए 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

अब तक, मैंने विभिन्न सरल कार्यों की कोशिश की है, लेकिन .applyलगता है कि विधि केवल एक पंक्ति को रिटर्न मान के रूप में स्वीकार करती है जब यह एक अक्ष पर उपयोग किया जाता है, और मुझे .transformकाम नहीं मिल सकता है । किसी भी सुझाव को सराहा जाएगा!

उदाहरण डेटा:

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}])

मुझे पता है कि यह काम नहीं करेगा क्योंकि हम डेटाफ़्रेम मेटा-डेटा को सुन्न से गुज़रते हुए खो देते हैं, लेकिन इससे आपको यह अहसास होना चाहिए कि मैंने क्या करने की कोशिश की:

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
इस पृष्ठ पर अन्य समाधान काम कर रहे हैं, लेकिन मुझे एक छोटा और प्रभावी निम्नलिखित मिला। stackoverflow.com/questions/27263805/…
desaiankitb

1
इस पृष्ठ पर पहुंचने वाले और कई कॉलम रखने वाले समाधान की तलाश में, इस प्रश्न पर एक नज़र डालें: stackoverflow.com/questions/17116814/…
Sos

जवाबों:


81

इस जैसे किसी और के बारे में क्या राय है:

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

फिर आपको सिर्फ कॉलम का नाम बदलना होगा


1
लगता है कि यह काम करने वाला है। आपकी सहायताके लिए धन्यवाद! सामान्य तौर पर, हालांकि, स्प्लिट-अप्लाई-कॉम्बाइन के लिए एक पसंदीदा तरीका है जहां लागू करें मनमाना आकार (लेकिन सभी विखंडू के अनुरूप) का डेटाफ्रेम लागू करता है, और कॉम्बिने सिर्फ रिटर्न किए गए डीएफएस को vstacks करता है?
विंसेंट

GroupBy.apply को काम करना चाहिए (मैंने इसे मास्टर के खिलाफ कोशिश की)। हालाँकि, इस मामले में आपको वास्तव में समूहीकरण के अतिरिक्त चरण से गुजरने की आवश्यकता नहीं है क्योंकि आप पंक्ति द्वारा डेटा को सही तरीके से उत्पन्न कर रहे हैं?
चांग शी

1
हे लोगों। यह इतनी देर से कूदने के लिए क्षमा करें, लेकिन सोचें कि क्या इससे बेहतर समाधान नहीं है। मैं पहली बार iterrows के साथ प्रयोग करने की कोशिश कर रहा हूं क्योंकि इसके लिए यह टिकट की तरह लगता है। प्रस्तावित समाधान से मैं भी भ्रमित हूं। "_" क्या दर्शाता है? क्या आप संभवतः बता सकते हैं कि समाधान कैसे काम करता है?
०१ अनुपात

11
क्या समाधान को दो से अधिक स्तंभों तक बढ़ाया जा सकता है?
राशिफल 1701

1
कृपया इस
सदिश

146

UPDATE2: अधिक जेनेरिक वेक्टराइज्ड फंक्शन, जो मल्टीपल normalऔर मल्टीपल listकॉलम के लिए काम करेगा

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

डेमो:

एकाधिक listकॉलम - सभी listकॉलम में प्रत्येक पंक्ति में समान # तत्व होने चाहिए:

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

मूल सूचकांक मानों को संरक्षित करना:

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

सेट अप:

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 कॉलम:

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

इस छोटी सी ट्रिक का उपयोग करके हम CSV जैसे कॉलम को 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

अद्यतन: सामान्य सदिश दृष्टिकोण (कई कॉलम के लिए भी काम करेगा):

मूल DF:

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

उपाय:

पहले CSV स्ट्रिंग्स को सूचियों में बदलें:

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

अब हम यह कर सकते हैं:

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

OLD उत्तर:

@AFinkelstein समाधान से प्रेरित , मैं इसे थोड़ा अधिक सामान्य बनाना चाहता था जिसे DF पर दो से अधिक स्तंभों के साथ लागू किया जा सकता है और साथ ही तेज़ी से, लगभग रूप में, एफ़िंकेलस्टीन के समाधान के रूप में तेजी से):

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
यार, अगर आप गिट पांडा में एक चर्चा खोल सकते हैं, मुझे लगता है कि हमें इस तरह के कार्य की आवश्यकता है !!! मैंने SO में पांडा के लिए अनलिस्टिंग और अनसोल्डिंग के बारे में बहुत सारे सवाल देखे हैं
YOBEN_S

कई कॉलम के लिए इसका उपयोग कैसे करें। जैसे अगर मैंने 2 कॉलम में कॉमा को अलग किया है और इसे अनुक्रम में करना चाहता हूं?
जसकरन सिंह पुरी

@JaskaranSinghPuri, आप पहले सूची में सभी CSV कॉलम बदलना चाहते हैं।
मैक्स

1
यदि आपकी सूची तत्व ट्यूपल्स हैं, तो अनजाने में, यह काम नहीं करता है। लेकिन पूरे टपल को स्ट्रिंग में परिवर्तित करने के बाद, यह एक आकर्षण की तरह काम करता है!
गुइडो

2
ऐसा लगता है कि वेनबेन की याचिका को पंडा देवताओं ने सुना था, उन्होंने .explode()एपीआई में एक विधि स्थापित की है ( यह उत्तर भी देखें )।
cs95

117

स्वीकार किए गए उत्तर की तुलना में तेजी से कुछ खोजने के लिए दर्दनाक प्रयोग के बाद, मुझे यह काम करने के लिए मिला। मैंने जिस पर इसे आज़माया था, उस पर यह 100x तेज़ी से भाग गया।

यदि कोई व्यक्ति इसे और अधिक सुरुचिपूर्ण बनाने का तरीका जानता है, तो हर तरह से कृपया मेरे कोड को संशोधित करें। मुझे ऐसा कोई तरीका नहीं मिला जो अन्य कॉलमों को सेट किए बिना काम करता हो, जिन्हें आप अनुक्रमणिका के रूप में रखना चाहते हैं और फिर अनुक्रमणिका को रीसेट कर रहे हैं और स्तंभों को फिर से नाम दे रहे हैं, लेकिन मुझे लगता है कि कुछ और है जो काम करता है।

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
इस समाधान ने काफी तेजी से काम किया और कम मेमोरी का उपयोग करने के लिए प्रतीत होता है,
सिरिल

1
यह एक अच्छा सदिश पांडा समाधान है, मैं उसी की तलाश में था। धन्यवाद!
डेनिस गोलोमेज़ोव

जब मैं अपने स्वयं के डेटासेट पर यह कोशिश करता हूं, तो मैं TypeError: object of type 'float' has no len()पहले चरण पर DataFrame(df.var1.str.split(',').tolist())
पहुंचता रहता हूं

@ user5359531 आपके डेटासेट में संभवतः NaNउस कॉलम में कुछ है, इसलिए प्रतिस्थापन हैb = DataFrame(a.var1.str.split(',').values.tolist(), index=a.var2).stack()
फ्लेयर

उदाहरण के साथ इस समाधान का सिर्फ एक अच्छा लेखन है।
22

46

यहाँ एक समारोह है जो मैंने इस सामान्य कार्य के लिए लिखा है । यह Series/ stackविधियों की तुलना में अधिक कुशल है । कॉलम ऑर्डर और नाम बरकरार रखे गए हैं।

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

इस फ़ंक्शन के साथ, मूल प्रश्न इस प्रकार है:

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

1
यह तेजी से फफोला है! इसके लिए बहुत - बहुत धन्यवाद।
अनुराग एन। शर्मा

42

पंडों> = 0.25

श्रृंखला और डेटाफ़्रेम विधियों एक .explode()विधि को परिभाषित करती है जो अलग-अलग पंक्तियों में सूचियों को विस्फोट करती हैसूची-जैसे कॉलम को एक्सप्लोर करने पर डॉक्स अनुभाग देखें ।

चूंकि आपके पास अल्पविराम से अलग तार की एक सूची है, इसलिए तत्वों की सूची प्राप्त करने के लिए अल्पविराम पर स्ट्रिंग को विभाजित करें, फिर explodeउस स्तंभ पर कॉल करें।

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

ध्यान दें कि explodeकेवल एक कॉलम (अभी के लिए) पर काम करता है


NaN और खाली सूचियों में वे उपचार मिलते हैं जिनके बिना आप सही होने के लिए हुप्स से कूदते हैं।

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

यह ravel+repeat -आधारित समाधानों पर एक गंभीर लाभ है (जो पूरी तरह से खाली सूचियों को अनदेखा करता है, और NaN पर चोक होता है)।


4
यह सबसे आसान है और मेरे मामले में सबसे उपयुक्त है! धन्यवाद!
इसहाक सिम

14

इसी तरह का प्रश्न: पांडा: मैं एक कॉलम में टेक्स्ट को कई पंक्तियों में कैसे विभाजित कर सकता हूं?

तुम यह कर सकते थे:

>> 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
यह एक और नाम बदलने के कोड के बाद काम करता है s.name = 'var1'
जेसी

14

टी एल; डॉ

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

प्रदर्शन

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

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

आइए एक नई डेटाफ्रेम बनाएं dजिसमें सूचियां हैं

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

सामान्य टिप्पणियाँ

मैं इस्तेमाल करेंगे np.arangeसाथ repeatdataframe सूचकांक पदों है कि मैं के साथ उपयोग कर सकते हैं का निर्माण करने के iloc

सामान्य प्रश्न

मैं उपयोग क्यों नहीं करता loc?

क्योंकि अनुक्रमणिका अद्वितीय नहीं हो सकती है और उपयोग करने वाले locप्रत्येक पंक्ति को लौटाएंगे जो एक अनुक्रमणिका सूचकांक से मेल खाती है।

आप valuesविशेषता और स्लाइस का उपयोग क्यों नहीं करते हैं ?

कॉल करते समय values, यदि डेटाफ्रेम की संपूर्णता एक सामंजस्यपूर्ण "ब्लॉक" में है, तो पंडों को उस सरणी का एक दृश्य दिखाई देगा जो "ब्लॉक" है। अन्यथा पंडों को एक नए सरणी के साथ मिलकर संघर्ष करना होगा। जब cobbling, वह सरणी एक समान dtype का होना चाहिए। अक्सर इसका मतलब है कि dtype के साथ एक सरणी वापस करना object। का उपयोग करके ilocबजाय टुकड़ा करने की क्रिया valuesविशेषता, मैं अपने आप को उस के साथ निपटने के लिए होने से कम।

आप क्यों इस्तेमाल करते हैं assign?

जब मैं assignउसी कॉलम नाम का उपयोग कर रहा हूं जिसे मैं विस्फोट कर रहा हूं, तो मैं मौजूदा कॉलम को ओवरराइट कर देता हूं और डेटाफ्रेम में अपनी स्थिति बनाए रखता हूं।

सूचकांक मूल्यों को क्यों दोहराया जाता है?

ilocदोहराया पदों पर उपयोग करने के आधार पर, परिणामी सूचकांक उसी दोहराया पैटर्न को दर्शाता है। प्रत्येक तत्व सूची या स्ट्रिंग के लिए दोहराता है।
इसके साथ रीसेट किया जा सकता हैreset_index(drop=True)


स्ट्रिंग्स के लिए

मैं समय से पहले स्ट्रिंग्स को विभाजित करना नहीं चाहता। इसलिए इसके बजाय मैंने sepयह मानते हुए तर्क की घटनाओं को गिना कि अगर मुझे विभाजित करना है, तो परिणामी सूची की लंबाई विभाजकों की संख्या से एक अधिक होगी।

मैं तो उस का उपयोग sepकरने के लिए joinतार तो 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)})

सूचियों के लिए

तार के लिए भी ऐसा sepही है , क्योंकि मुझे इसके पहले से विभाजित होने की घटनाओं की गणना करने की आवश्यकता नहीं है ।

मैं concatenateएक साथ सूचियों को जाम करने के लिए Numpy का उपयोग करता हूं ।

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


मैं यह पसंद है। वास्तव में संक्षिप्त और प्रदर्शन वास्तव में अच्छा होना चाहिए। एक सवाल हालांकि: df.iloc [i] डेटाफ्रेम की पंक्तियों को दोहराने के समान है या क्या यह उससे अधिक कुशल है? धन्यवाद!
टिम

7

डेटाफ्रेम की संरचना को बदले बिना डेटाफ्रेम को विभाजित और विस्फोट करने की संभावना है

विशिष्ट स्तंभों का डेटा विभाजित और विस्तारित करें

इनपुट:

    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)

बाहर:

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

संपादित करें -1

एकाधिक स्तंभों के लिए पंक्तियों का विभाजन और विस्तार

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]

संदर्भ स्तंभ के आधार पर पुन: अनुक्रमण करना और स्टैक के साथ स्तंभ मान की जानकारी को संरेखित करना

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

बाहर:

                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

मैं स्तंभों की मनमानी संख्या के साथ डेटाफ्रेम के लिए एक समाधान के साथ आया था (जबकि अभी भी एक समय में केवल एक कॉलम की प्रविष्टियों को अलग कर रहा हूं)।

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
इस निर्णय () रूपांतरण के कारण अच्छा लेकिन दुखद धीमा है :(
MAQ

4

यहां एक बिल्कुल सीधा संदेश है जो splitपांडा एक्सेसर से विधि का उपयोग करता है strऔर फिर प्रत्येक पंक्ति को एक एकल सरणी में समतल करने के लिए NumPy का उपयोग करता है।

गैर-विभाजित कॉलम को सही समय के साथ दोहराते हुए संबंधित मानों को पुनः प्राप्त किया जाता है 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
यह एक बहुत ही सुंदर जवाब हो सकता है। दुर्भाग्य से, यह बहुत सारे स्तंभों के लिए पैमाने पर नहीं है, करता है?
माइकल डॉर्न

3

मैं अपनी सूचियों को विस्फोट करने के लिए विभिन्न तरीकों का उपयोग करके आउट-ऑफ-मेमोरी अनुभव के साथ संघर्ष कर रहा हूं, इसलिए मैंने कुछ बेंचमार्क तैयार किए जो मुझे यह तय करने में मदद करें कि कौन से उत्तर को उत्थान करना है। मैंने सूची की लंबाई के अनुपात में सूचियों की संख्या के साथ पाँच परिदृश्यों का परीक्षण किया। नीचे दिए गए परिणाम साझा करना:

समय: (कम बेहतर है, बड़े संस्करण को देखने के लिए क्लिक करें)

गति

पीक मेमोरी उपयोग: (कम बेहतर है)

पीक मेमोरी उपयोग

निष्कर्ष :

  • @ MaxU के जवाब (अद्यतन 2), कोडनेम CONCATENATE प्रदान करता है, लगभग हर मामले में सबसे अच्छी गति है, जबकि झांकना स्मृति के उपयोग को कम रखने,
  • देखें @ DMulligan का उत्तर (कोडनाम स्टैक ) यदि आपको अपेक्षाकृत छोटी सूचियों के साथ बहुत सी पंक्तियों को संसाधित करने की आवश्यकता है और वे उच्च शिखर को बढ़ा सकते हैं, तो
  • स्वीकृत @ चांग का जवाब उन डेटा फ़्रेमों के लिए अच्छी तरह से काम करता है जिनकी कुछ पंक्तियाँ हैं लेकिन बहुत बड़ी सूचियाँ हैं।

पूर्ण विवरण (फ़ंक्शन और बेंचमार्किंग कोड) इस GitHub gist में हैं । कृपया ध्यान दें कि बेंचमार्क समस्या का सरलीकरण किया गया था और इसमें स्ट्रिंग्स के विभाजन को सूची में शामिल नहीं किया गया था - जो कि अधिकांश समाधान इसी तरह से किए गए थे।


अच्छा तुलना! क्या आपको एक कोड पोस्ट करने का मन है, जिसे आपने बेंचमार्क प्लॉट करने के लिए इस्तेमाल किया है?
मैक्सयू

1
कृपया इस लिंक को देखें: gist.github.com/krassowski/0259a2cd2ba774ccd9f69bbcc3187fbf (पहले से ही उत्तर में शामिल) - IMO यह सब यहाँ पेस्ट करने के लिए थोड़ा लंबा होगा।
krassowski

2

उत्कृष्ट @ DMulligan के समाधान के आधार पर , यहां एक जेनेरिक वेक्टराइज्ड (नो लूप्स) फंक्शन है जो एक डेटाफ्रेम के एक कॉलम को कई पंक्तियों में विभाजित करता है, और इसे मूल डेटाफ्रेम में वापस मर्ज करता है। यह change_column_orderइस उत्तर से एक महान सामान्य कार्य का भी उपयोग करता है ।

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)

उदाहरण:

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    

ध्यान दें कि यह स्तंभों के मूल सूचकांक और आदेश को संरक्षित करता है। यह डेटाफ्रेम के साथ भी काम करता है जिसमें गैर-अनुक्रमिक सूचकांक होता है।


2
यह मेरे लिए यह एक दरार है, अच्छा काम: stackoverflow.com/a/48554655/6672746
इवान

2

स्ट्रिंग फ़ंक्शन विभाजन एक विकल्प बूलियन तर्क 'विस्तार' ले सकता है।

यहाँ इस तर्क का उपयोग कर एक समाधान है:

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

1

बस ऊपर से जिलन के उत्कृष्ट उत्तर का उपयोग किया गया था, लेकिन कई स्तंभों को विभाजित करने के लिए विस्तार करने की आवश्यकता थी। सोचा था कि साझा करूंगा।

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

MultiIndex समर्थन के साथ MaxU के उत्तर को उन्नत किया

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

एक-लाइनर का उपयोग कर split(___, expand=True)और levelऔर nameतर्कों को 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

यदि आपको bप्रश्न में बिल्कुल दिखने की आवश्यकता है, तो आप इसके अतिरिक्त कर सकते हैं:

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

मैं इस समस्या के निम्नलिखित समाधान के साथ आया हूँ:

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

एक और समाधान जो अजगर कॉपी पैकेज का उपयोग करता है

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

यहाँ बहुत सारे उत्तर हैं, लेकिन मुझे आश्चर्य है कि किसी ने पंडों में विस्फोट कार्य का उल्लेख नहीं किया है। नीचे दिए गए लिंक की जाँच करें: https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.explode.html#pandas.DataFrame.excode

किसी कारण से मैं उस फ़ंक्शन का उपयोग करने में असमर्थ था, इसलिए मैंने नीचे दिए गए कोड का उपयोग किया:

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

यहां छवि विवरण दर्ज करें

ऊपर मेरे डेटा का एक नमूना है। जैसा कि आप देख सकते हैं कि लोगों के कॉलम में लोगों की श्रृंखला थी, और मैं इसे विस्फोट करने की कोशिश कर रहा था। कोड मैंने सूची प्रकार डेटा के लिए काम दिया है। इसलिए अपने अल्पविराम से अलग पाठ डेटा को सूची प्रारूप में लाने का प्रयास करें। इसके अलावा, चूंकि मेरा कोड फंक्शंस में बनाया गया है, इसलिए यह कस्टम / अप्लाई फंक्शंस की तुलना में बहुत तेज है।

नोट: आपको पाइप के साथ pandas_explode स्थापित करने की आवश्यकता हो सकती है।


0

मेरे पास एक समान समस्या थी, मेरा समाधान डेटाफ्रेम को पहले शब्दकोशों की सूची में परिवर्तित कर रहा था, फिर संक्रमण करना। यहाँ समारोह है:

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)

उदाहरण:

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

आप सूची प्रकार पंक्तियों को अलग करने के लिए फ़ंक्शन को थोड़ा बदल सकते हैं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.