পান্ডাস: আমি কীভাবে একাধিক সারিতে একটি কলামে পাঠ্যকে বিভক্ত করব?


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ফাংশন এবং দ্রুত ম্যাক্রোর সাহায্যে এটি এক্সেলে করতে পারি , তবে আমার ডেটাসেটটিতে এক্সেল হ্যান্ডেল করার জন্য অনেক বেশি রেকর্ড রয়েছে।

শেষ পর্যন্ত, আমি যেমন জন লেননের রেকর্ড নিতে এবং একাধিক লাইন তৈরি করতে চাই, প্রতিটি লাইনের পৃথক লাইনের সিটের তথ্য দিয়ে।


এই দুর্দান্ত প্রশ্নটি পান্ডাসের ফ্ল্যাটম্যাপের সাথে সম্পর্কিত, যা বর্তমানে বিদ্যমান নেই
সিডারলিন্ট

উত্তর:


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
@ ডান অ্যালান যখন আপনি আবেদন করেন তখন সিরিজে একটি সূচি দেয়; তারা কলামের নাম হয়ে যাবে
জেফ

4
এটি যখন প্রশ্নের উত্তর দেয়, তবে এটি অবশ্যই উল্লেখযোগ্য যে (সম্ভবত) বিভাজন () প্রতিটি সারিটির জন্য একটি তালিকা তৈরি করে, যা DataFrameখুব দ্রুত আকারের আকারকে উড়িয়ে দেয় । আমার ক্ষেত্রে, M 200M টেবিলটিতে কোড চালানোর ফলে 10 ডলার মেমরি (+ স্ব্যাপ ...) ব্যবহারের ফলস্বরূপ।
ডেভিড নেমেস্কি

1
যদিও আমি নিশ্চিত নই যে এটি কারণ split(), কারণ কেবলমাত্র reduce()কলামটির মাধ্যমে আইএনএন একটি কবজির মতো কাজ করে। সমস্যাটি তখনই থাকতে পারে stack()...
ডেভিড নেমেস্কি

4
আমি এর NameError: name 'Series' is not definedজন্য ত্রুটি পাচ্ছি । কোথায় Seriesথেকে আসে অনুমিত? সম্পাদনা: কিছুই নয়, pandas.Seriesযেহেতু এটি আইটেমটির উল্লেখ করছেpandas
ইউজার 35359531

2
হ্যাঁ, @ ব্যবহারকারী 5359531। আমি 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 গুণ দ্রুততর করার জন্য এটি যথেষ্ট। তবে তৃতীয় সমাধানটি, যা কিছুটা ব্যঙ্গাত্মকভাবে স্ট্রিংস্প্লিটকে কল করতে প্রচুর অপচয় করে () এটি প্রতিটি সারিতে প্রতি কলামে একবার বলা হয়, সুতরাং অন্য দুটি সমাধানের চেয়ে তিনগুণ বেশি), প্রথমটির চেয়ে প্রায় 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()"

খুব কাজ করে, এবং প্রায় হিসাবে দক্ষ।

সম্পাদনা: আরও সহজ ! এবং NaNs পরিচালনা করে (তবে কম দক্ষ):

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

এই পদ্ধতিটি যে পরিমাণ স্মৃতি গ্রহণ করে তা নিয়ে আমার একটু সমস্যা হচ্ছে এবং আমি ভাবছি আপনি যদি আমাকে একটু পরামর্শ দিতে পারেন। আমার কাছে একটি ডেটাফ্রেম রয়েছে যাতে প্রায় 8000 সারি থাকে, যার প্রতিটি স্ট্রিং 9216 স্পেস ডিলিমেটেড 8-বিট ইন্টিজার সহ থাকে। এটি মোটামুটি 75MB, তবে আমি যখন সর্বশেষ সমাধানটি ভারব্যাটিক প্রয়োগ করি তখন পাইথন আমার 2 জিবি স্মরণ করে e আপনি কি আমাকে এমন কোনও উত্সের দিকে নির্দেশ করতে পারেন যা আমাকে বলবে যে এটি কেন, এবং আমি এর কাছাকাছি আসতে কী করতে পারি? ধন্যবাদ।
দুর্গ-ব্র্যাভো

1
আপনার কাছে প্রচুর তালিকাগুলি এবং খুব ছোট স্ট্রিং রয়েছে, যা পাইথন (এবং মধ্যবর্তী পদক্ষেপ ".স্প্লিট ()। টোলিস্ট ()" খাঁটি পাইথন অবজেক্ট তৈরি করে) এর স্মৃতি ব্যবহারের জন্য কমবেশি সবচেয়ে খারাপ পরিস্থিতি। আমি সম্ভবত আপনার জায়গায় যা করব তা হ'ল কোনও ফাইলের ডেটা ফ্রেম ডাম্প করা এবং তারপরে এটি সিডিভি হিসাবে রিড_সিএসভি (..., সেপ = '') দিয়ে খোলা। তবে বিষয়টিতে দাঁড়ানোর জন্য: প্রথম সমাধান (তৃতীয়টির সাথে একসাথে, যা দুর্দান্তভাবে ধীরে ধীরে হওয়া উচিত) হতে পারে যেটি আপনাকে 4 এর মধ্যে সর্বনিম্ন স্মৃতি ব্যবহারের প্রস্তাব দিচ্ছে, যেহেতু আপনার তুলনায় অপেক্ষাকৃত দীর্ঘ সারি রয়েছে।
পিট্রো ব্যাটিস্টন

আরে পিয়েট্রো, আমি আপনার ফাইলটি সংরক্ষণ এবং পুনরায় লোড করার পরামর্শটি চেষ্টা করেছি, এটি বেশ ভাল কাজ করেছে। আমি স্ট্রিংআইও অবজেক্টে এটি করার চেষ্টা করার সময় আমি কিছুটা সমস্যার মধ্যে পড়েছিলাম এবং আমার সমস্যার একটি দুর্দান্ত সমাধান এখানে পোস্ট করা হয়েছে
দুর্গ-ব্র্যাভো

3
আপনার শেষ পরামর্শটি tolist()নিখুঁত। আমার ক্ষেত্রে আমি তালিকার কেবলমাত্র একটি টুকরো ডেটা চেয়েছিলাম এবং Ix ব্যবহার করে সরাসরি আমার বিদ্যমান ডিএফ-তে একটি একক কলাম যুক্ত করতে সক্ষম হয়েছি:df['newCol'] = pd.DataFrame(df.col.str.split().tolist()).ix[:,2]
চমত্কার

আহ্, প্রথমে এটি কাজ করতে আমার অসুবিধা হচ্ছিল - এমন কিছু সম্পর্কে obect of type 'float' has no len()যা অবাক হয়ে যাচ্ছিল, যতক্ষণ না আমি বুঝতে পারি যে আমার কয়েকটি সারি তাদের NaNমধ্যে রয়েছে, এর বিপরীতে str
ডোয়ান্ডারসন

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()
হল্জকোহেলিংিল

@ হোলজকোহলংগ্রিল - মন্তব্যের জন্য আপনাকে ধন্যবাদ, আমি উত্তর দেওয়ার জন্য এটি যুক্ত করি।
jezrael

@ জেজরেল, এই কোডটি কার্যকর করতে আমার খুব বেশি সময় লাগছে, এটি প্রত্যাশিত। আমি কীভাবে এটি দ্রুততর করব? যদি আমি এটিকে লুপের মতো রাখি: এক্স ইন ডিএফ [সিব্লকস] [: 100] এর জন্য কেবল এটি একটি সাবসেটে করা এবং তারপরে এই সাবসেটগুলিতে সম্মতি জানানো, কাজটি কি কাজ করবে?
bernando_vialli

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 এ, বি, সি এর জন্য আপনার প্রত্যাশিত ফলাফলগুলি কী?
বেন2018

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.