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


135

मैं एक बड़ी सीएसएसवी फाइल के साथ काम कर रहा हूं और अगले कॉलम में टेक्स्ट का एक स्ट्रिंग है जिसे मैं एक विशिष्ट सीमांकक द्वारा विभाजित करना चाहता हूं। मैं सोच रहा था कि क्या पंडों या अजगर का उपयोग करने का एक सरल तरीका है?

CustNum  CustomerName     ItemQty  Item   Seatblocks                 ItemExt
32363    McCartney, Paul      3     F04    2:218:10:4,6                   60
31316    Lennon, John        25     F01    1:13:36:1,12 1:13:37:1,13     300

मैं अंतरिक्ष (' ')और फिर स्तंभ (':')में बृहदान्त्र से विभाजित करना चाहता हूं Seatblocks, लेकिन प्रत्येक सेल में एक अलग संख्या में कॉलम होगा। मेरे पास कॉलम को पुनर्व्यवस्थित करने के लिए एक फ़ंक्शन है ताकि Seatblocksकॉलम शीट के अंत में हो, लेकिन मुझे यकीन नहीं है कि वहां से क्या करना है। मैं इसे एक्सेल में निर्मित text-to-columnsफंक्शन और एक क्विक मैक्रो के साथ कर सकता हूं , लेकिन मेरे डेटासेट में एक्सेल को संभालने के लिए बहुत सारे रिकॉर्ड हैं।

अंत में, मैं इस तरह के जॉन लेनन का रिकॉर्ड लेना चाहता हूं और एक अलग लाइन पर सीटों के प्रत्येक सेट से जानकारी के साथ कई लाइनें बनाना चाहता हूं।


यह महान प्रश्न फ्लैटपांड से संबंधित है पांडा में, जो वर्तमान में मौजूद नहीं है
cdarlint

जवाबों:


203

यह स्पेस द्वारा सीटब्लॉक्स को विभाजित करता है और प्रत्येक को अपनी पंक्ति देता है।

In [43]: df
Out[43]: 
   CustNum     CustomerName  ItemQty Item                 Seatblocks  ItemExt
0    32363  McCartney, Paul        3  F04               2:218:10:4,6       60
1    31316     Lennon, John       25  F01  1:13:36:1,12 1:13:37:1,13      300

In [44]: s = df['Seatblocks'].str.split(' ').apply(Series, 1).stack()

In [45]: s.index = s.index.droplevel(-1) # to line up with df's index

In [46]: s.name = 'Seatblocks' # needs a name to join

In [47]: s
Out[47]: 
0    2:218:10:4,6
1    1:13:36:1,12
1    1:13:37:1,13
Name: Seatblocks, dtype: object

In [48]: del df['Seatblocks']

In [49]: df.join(s)
Out[49]: 
   CustNum     CustomerName  ItemQty Item  ItemExt    Seatblocks
0    32363  McCartney, Paul        3  F04       60  2:218:10:4,6
1    31316     Lennon, John       25  F01      300  1:13:36:1,12
1    31316     Lennon, John       25  F01      300  1:13:37:1,13

या, प्रत्येक बृहदान्त्र-पृथक स्ट्रिंग को अपने कॉलम में देने के लिए:

In [50]: df.join(s.apply(lambda x: Series(x.split(':'))))
Out[50]: 
   CustNum     CustomerName  ItemQty Item  ItemExt  0    1   2     3
0    32363  McCartney, Paul        3  F04       60  2  218  10   4,6
1    31316     Lennon, John       25  F01      300  1   13  36  1,12
1    31316     Lennon, John       25  F01      300  1   13  37  1,13

यह थोड़ा बदसूरत है, लेकिन शायद कोई पूर्ववर्ती समाधान के साथ झंकार करेगा।


7
जब आप आवेदन करते हैं तो @DanAllan श्रृंखला को एक सूचकांक देते हैं; वे स्तंभ नाम बन जाएंगे
जेफ

4
हालांकि यह प्रश्न का उत्तर देता है, यह ध्यान देने योग्य है कि (शायद) विभाजन () प्रत्येक पंक्ति के लिए एक सूची बनाता है, जो DataFrameबहुत जल्दी आकार को उड़ा देता है । मेरे मामले में, ~ 200M टेबल पर कोड चलाने से ~ 10G मेमोरी (+ स्वैप ...) का उपयोग होता है।
डेविड नेमेस्की

1
हालांकि मुझे यकीन नहीं है कि यह इस वजह से है split(), क्योंकि reduce()कॉलम के माध्यम से 'आईएनजी' एक आकर्षण की तरह काम करता है। समस्या तब झूठ हो सकती है stack()...
डेविड नेमसेक

4
मुझे इसके लिए त्रुटि मिल रही है NameError: name 'Series' is not defined। कहाँ Seriesसे आना है? संपादित करें: कोई बात नहीं, यह होना चाहिए pandas.Seriesक्योंकि यह आइटम से बात कर रहा हैpandas
user5359531

2
हां, @ user5359531। मैं from pandas import Seriesसुविधा / संक्षिप्तता के लिए।
दान एलन

52

दान से अलग, मैं उनके जवाब को काफी सुरुचिपूर्ण मानता हूं ... लेकिन दुर्भाग्य से यह बहुत ही अक्षम भी है। इसलिए, चूंकि प्रश्न "एक बड़ी सीएसवी फ़ाइल" का उल्लेख किया गया है , मुझे शेल डैन के समाधान में प्रयास करने का सुझाव दें:

time python -c "import pandas as pd;
df = pd.DataFrame(['a b c']*100000, columns=['col']);
print df['col'].apply(lambda x : pd.Series(x.split(' '))).head()"

... इस विकल्प की तुलना में:

time python -c "import pandas as pd;
from scipy import array, concatenate;
df = pd.DataFrame(['a b c']*100000, columns=['col']);
print pd.DataFrame(concatenate(df['col'].apply( lambda x : [x.split(' ')]))).head()"

... और इस:

time python -c "import pandas as pd;
df = pd.DataFrame(['a b c']*100000, columns=['col']);
print pd.DataFrame(dict(zip(range(3), [df['col'].apply(lambda x : x.split(' ')[i]) for i in range(3)]))).head()"

दूसरा बस 100 000 श्रृंखला आवंटित करने से बचता है, और यह इसे लगभग 10 गुना तेज बनाने के लिए पर्याप्त है। लेकिन तीसरा समाधान, जो कुछ विडंबना यह है कि str.split () के लिए बहुत सारी कॉल बर्बाद करता है (इसे प्रति पंक्ति एक बार प्रति कॉलम कहा जाता है, इसलिए अन्य दो समाधानों की तुलना में तीन गुना अधिक है), चारों ओर है पहले की तुलना में 40 गुना तेज है, क्योंकि यह भी 100 000 सूची उदाहरण के लिए बचा जाता है। और हाँ, यह निश्चित रूप से थोड़ा बदसूरत है ...

संपादित करें: यह उत्तर बताता है कि "to_list ()" का उपयोग कैसे करें और लंबोदर की आवश्यकता से कैसे बचें। परिणाम कुछ इस तरह है

time python -c "import pandas as pd;
df = pd.DataFrame(['a b c']*100000, columns=['col']);
print pd.DataFrame(df.col.str.split().tolist()).head()"

जो तीसरे समाधान की तुलना में अधिक कुशल है, और निश्चित रूप से बहुत अधिक सुरुचिपूर्ण है।

संपादित करें: और भी सरल

time python -c "import pandas as pd;
df = pd.DataFrame(['a b c']*100000, columns=['col']);
print pd.DataFrame(list(df.col.str.split())).head()"

भी काम करता है, और है लगभग है कुशल है।

संपादित करें: और भी सरल ! और NaN को संभालता है (लेकिन कम कुशल):

time python -c "import pandas as pd;
df = pd.DataFrame(['a b c']*100000, columns=['col']);
print df.col.str.split(expand=True).head()"

मुझे स्मृति की मात्रा से थोड़ी परेशानी हो रही है जो इस पद्धति का उपभोग करती है और मैं सोच रहा हूं कि क्या आप मुझे थोड़ी सलाह दे सकते हैं। मेरे पास एक DataFrame है जिसमें लगभग 8000 पंक्तियाँ हैं, प्रत्येक में एक स्ट्रिंग है जिसमें 9216 स्पेस 8-बिट पूर्णांक को सीमांकित किया गया है। यह लगभग 75 एमबी है, लेकिन जब मैं अंतिम समाधान शब्दशः लागू करता हूं, तो पायथन मेरी 2GB मेमोरी खा जाता है। क्या आप मुझे कुछ स्रोत की दिशा में इंगित कर सकते हैं जो मुझे बताएगा कि यह क्यों है, और इसके चारों ओर पाने के लिए मैं क्या कर सकता हूं? धन्यवाद।
महल-ब्रावो

1
आपके पास बहुत सारी सूचियां और बहुत छोटे तार हैं, जो कि अजगर में (और मध्यवर्ती चरण ".split () () () (") "शुद्ध अजगर वस्तुओं का उत्पादन करता है" में मेमोरी के उपयोग के लिए सबसे खराब स्थिति है। मैं शायद आपकी जगह पर क्या करूँगा डेटाफ़्रेम को फ़ाइल में डंप करने के लिए, और फिर इसे cs_ के रूप में read_csv (..., sep = '') के साथ खोलें। लेकिन विषय पर बने रहने के लिए: पहला समाधान (तीसरे के साथ, जो कि हालांकि थोड़ा धीमा होना चाहिए) आपको 4 में से सबसे कम मेमोरी उपयोग की पेशकश कर सकता है, क्योंकि आपके पास अपेक्षाकृत लंबी पंक्तियों की एक छोटी संख्या है।
पिएत्रो बेटिस्टन

हे पिएत्रो, मैंने एक फ़ाइल को सहेजने और फिर से लोड करने के आपके सुझाव की कोशिश की, यह काफी अच्छा काम किया। मैं कुछ परेशानी में भाग गया जब मैंने इसे एक स्ट्रिंग स्ट्रिंग ऑब्जेक्ट में करने की कोशिश की, और मेरी समस्या का एक अच्छा समाधान यहां पोस्ट किया गया है
महल-ब्रावो

3
आपका अंतिम सुझाव tolist()एकदम सही है। मेरे मामले में मैं केवल सूची में मौजूद डेटा के टुकड़ों में से एक चाहता था और सीधे .ix का उपयोग करके अपने मौजूदा एफएफ में एक कॉलम जोड़ने में सक्षम था:df['newCol'] = pd.DataFrame(df.col.str.split().tolist()).ix[:,2]
काल्पनिक जूल

आह, मैं मुसीबत पहली बार में काम करने के लिए इस हो रही हो रही थी - कुछ के बारे में obect of type 'float' has no len()जो चौंकाने वाला था, जब तक मुझे एहसास हुआ कि कुछ मेरी पंक्तियों की थी NaNउन में, के रूप में करने का विरोध किया str
dwanderson

14
import pandas as pd
import numpy as np

df = pd.DataFrame({'ItemQty': {0: 3, 1: 25}, 
                   'Seatblocks': {0: '2:218:10:4,6', 1: '1:13:36:1,12 1:13:37:1,13'}, 
                   'ItemExt': {0: 60, 1: 300}, 
                   'CustomerName': {0: 'McCartney, Paul', 1: 'Lennon, John'}, 
                   'CustNum': {0: 32363, 1: 31316}, 
                   'Item': {0: 'F04', 1: 'F01'}}, 
                    columns=['CustNum','CustomerName','ItemQty','Item','Seatblocks','ItemExt'])

print (df)
   CustNum     CustomerName  ItemQty Item                 Seatblocks  ItemExt
0    32363  McCartney, Paul        3  F04               2:218:10:4,6       60
1    31316     Lennon, John       25  F01  1:13:36:1,12 1:13:37:1,13      300

चेनिंग के साथ एक और समान समाधान उपयोग है reset_indexऔर rename:

print (df.drop('Seatblocks', axis=1)
             .join
             (
             df.Seatblocks
             .str
             .split(expand=True)
             .stack()
             .reset_index(drop=True, level=1)
             .rename('Seatblocks')           
             ))

   CustNum     CustomerName  ItemQty Item  ItemExt    Seatblocks
0    32363  McCartney, Paul        3  F04       60  2:218:10:4,6
1    31316     Lennon, John       25  F01      300  1:13:36:1,12
1    31316     Lennon, John       25  F01      300  1:13:37:1,13

यदि स्तंभ मान नहीं NaN हैं, तो सबसे तेज़ समाधान निर्माणकर्ता के listसाथ समझ का उपयोग है DataFrame:

df = pd.DataFrame(['a b c']*100000, columns=['col'])

In [141]: %timeit (pd.DataFrame(dict(zip(range(3), [df['col'].apply(lambda x : x.split(' ')[i]) for i in range(3)]))))
1 loop, best of 3: 211 ms per loop

In [142]: %timeit (pd.DataFrame(df.col.str.split().tolist()))
10 loops, best of 3: 87.8 ms per loop

In [143]: %timeit (pd.DataFrame(list(df.col.str.split())))
10 loops, best of 3: 86.1 ms per loop

In [144]: %timeit (df.col.str.split(expand=True))
10 loops, best of 3: 156 ms per loop

In [145]: %timeit (pd.DataFrame([ x.split() for x in df['col'].tolist()]))
10 loops, best of 3: 54.1 ms per loop

लेकिन यदि कॉलम में NaNकेवल str.splitपैरामीटर है expand=Trueजो रिटर्न DataFrame( प्रलेखन ) के साथ काम करता है , और यह समझाता है कि यह धीमा क्यों है:

df = pd.DataFrame(['a b c']*10, columns=['col'])
df.loc[0] = np.nan
print (df.head())
     col
0    NaN
1  a b c
2  a b c
3  a b c
4  a b c

print (df.col.str.split(expand=True))
     0     1     2
0  NaN  None  None
1    a     b     c
2    a     b     c
3    a     b     c
4    a     b     c
5    a     b     c
6    a     b     c
7    a     b     c
8    a     b     c
9    a     b     c

शायद यह उल्लेख के लायक है कि आपको उदाहरण के लिए उपयोग करते समय expand=Trueकाम करने वाले विकल्प की आवश्यकता है । pandas.DataFrames.str.split()
Holzkohlengrill

@holzkohlengrill - टिप्पणी के लिए धन्यवाद, मैं इसे जवाब देने के लिए जोड़ता हूं।
जीजेरेल

@ जेज़रेल, मुझे इस कोड को निष्पादित करने में बहुत समय लग रहा है, क्या यह अपेक्षित है। मैं वास्तव में इसे कैसे तेज करूं? अगर मैं इसे एक लूप में डाल दूं: जैसे कि x के लिए df [सीब्लॉक] [: 100] केवल एक सबसेट पर करूं और फिर इन सबसेट्स पर काम करूं, तो क्या वह काम करेगा?
बर्नान्डो_विल्ली

2

एक और दृष्टिकोण इस तरह होगा:

temp = df['Seatblocks'].str.split(' ')
data = data.reindex(data.index.repeat(temp.apply(len)))
data['new_Seatblocks'] = np.hstack(temp)

1

शामिल होने और स्टैक () की आवश्यकता के बिना भी ग्रुपबी () का उपयोग कर सकते हैं।

उदाहरण डेटा से ऊपर का उपयोग करें:

import pandas as pd
import numpy as np


df = pd.DataFrame({'ItemQty': {0: 3, 1: 25}, 
                   'Seatblocks': {0: '2:218:10:4,6', 1: '1:13:36:1,12 1:13:37:1,13'}, 
                   'ItemExt': {0: 60, 1: 300}, 
                   'CustomerName': {0: 'McCartney, Paul', 1: 'Lennon, John'}, 
                   'CustNum': {0: 32363, 1: 31316}, 
                   'Item': {0: 'F04', 1: 'F01'}}, 
                    columns=['CustNum','CustomerName','ItemQty','Item','Seatblocks','ItemExt']) 
print(df)

   CustNum     CustomerName  ItemQty Item                 Seatblocks  ItemExt
0  32363    McCartney, Paul  3        F04  2:218:10:4,6               60     
1  31316    Lennon, John     25       F01  1:13:36:1,12 1:13:37:1,13  300  


#first define a function: given a Series of string, split each element into a new series
def split_series(ser,sep):
    return pd.Series(ser.str.cat(sep=sep).split(sep=sep)) 
#test the function, 
split_series(pd.Series(['a b','c']),sep=' ')
0    a
1    b
2    c
dtype: object

df2=(df.groupby(df.columns.drop('Seatblocks').tolist()) #group by all but one column
          ['Seatblocks'] #select the column to be split
          .apply(split_series,sep=' ') # split 'Seatblocks' in each group
         .reset_index(drop=True,level=-1).reset_index()) #remove extra index created

print(df2)
   CustNum     CustomerName  ItemQty Item  ItemExt    Seatblocks
0    31316     Lennon, John       25  F01      300  1:13:36:1,12
1    31316     Lennon, John       25  F01      300  1:13:37:1,13
2    32363  McCartney, Paul        3  F04       60  2:218:10:4,6

अग्रिम में धन्यवाद। मैं दो स्तंभों को अलग-अलग विभाजित करके उपरोक्त कोड का उपयोग कैसे कर सकता हूं। उदाहरण के लिए: 0 31316 लेनन, जॉन 25 F01 300 1: 13: 36: 1,12 1: 13: 37: 1,13 ए, बी .. परिणाम होना चाहिए: 0 31316 Lennon, John 25 F01 300 1:13:36:1,12 Aऔर अगली पंक्ति 0 31316 Lennon, John 25 F01 300 1:13:37:1,13 B
कृति

@ कृति।, मैं प्रश्न को समझने की कोशिश करता हूं। क्या आपका मतलब है कि दो स्तंभों में बंटवारे के बाद सदस्यों की संख्या समान होनी चाहिए? 0 31316 लेनन, जॉन 25 F01 300 1: 13: 36: 1,12 1: 13: 37: 1,13 ए, बी, सी के लिए आपके अपेक्षित परिणाम क्या हैं?
Ben2018

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