मैं दो कॉलम (index_start और index_end) में संख्यात्मक मानों के आधार पर अपने डेटा फ्रेम का विस्तार करना चाहता हूं। मेरा डीएफ इस तरह दिखता है:

item  index_start  index_end    
A          1            3
B          4            7

मैं चाहता हूं कि ए के लिए पंक्तियों को 1 से 3 तक और पंक्तियों को 4 से 7 तक पंक्तियों को बनाने के लिए विस्तारित किया जाए।

item  index_start  index_end    index
A          1            3         1
A          1            3         2
A          1            3         3
B          4            7         4
B          4            7         5
B          4            7         6
B          4            7         7

यह सुनिश्चित नहीं है कि इसे पायथन/पांडा में कैसे कार्यान्वित किया जाए।

4
user3242036 25 अगस्त 2021, 18:44

3 जवाब

सबसे बढ़िया उत्तर

आप .explode() का उपयोग कर सकते हैं

df['index'] = df.apply(lambda row: list(range(row['index_start'], row['index_end']+1)), axis=1)
df.explode('index')

  item  index_start  index_end index
0    A            1          3     1
0    A            1          3     2
0    A            1          3     3
1    B            4          7     4
1    B            4          7     5
1    B            4          7     6
1    B            4          7     7
5
Andreas 25 अगस्त 2021, 18:50

हम उपयोग कर सकते हैं pd.Index.repeat index_end - index_start + 1 बार दोहराने के लिए rel="nofollow noreferrer">df.reindex। फिर index_start और index_end के बीच डेटा जेनरेट करें >itertools.starmap.

from itertools import starmap

rep = (df['index_end'] - df['index_start']) + 1
idx = df.index.repeat(rep)
vals = df.to_numpy()[:, 1:]
vals[:, 1] += 1
index = np.hstack([*starmap(np.arange, vals)])
df.reindex(idx).assign(index=index)

  item  index_start  index_end  index
0    A            1          3      1
0    A            1          3      2
0    A            1          3      3
1    B            4          7      4
1    B            4          7      5
1    B            4          7      6
1    B            4          7      7

@Andreas का एक अच्छा पठनीय समाधान है लेकिन दो अड़चन संचालन हैं जो रनटाइम को बढ़ाएंगे एक df.apply अक्ष 1 और df.explode पर।

समय विश्लेषण:

बेंचमार्किंग सेटअप:

df = pd.concat([df]*1000, ignore_index=True)

# @Ch3steR's solution
In [130]: %%timeit 
     ...: rep = (df['index_end'] - df['index_start']) + 1 
     ...: idx = df.index.repeat(rep) 
     ...: vals = df.to_numpy()[:, 1:] 
     ...: vals[:, 1] += 1 
     ...: index = np.hstack([*starmap(np.arange, vals)]) 
     ...: df.reindex(idx).assign(index=index) 
     ...:  
     ...:                                                                       
4.82 ms ± 72.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

# @Andreas' solution
In [131]: %%timeit 
     ...: df['index'] = df.apply(lambda row: list(range(row['index_start'], row[
     ...: 'index_end']+1)), axis=1) 
     ...: df.explode('index') 
     ...:  
     ...:                                                                       
20.4 ms ± 796 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

मैंने कुछ बार परीक्षण चलाया, मेरा समाधान लगभग ~ 4 गुना तेज है। हम गति में और सुधार कर सकते हैं यदि हम किसी तरह index = np.hstack([*starmap(np.arange, vals)]) इस ऑपरेशन को वेक्टरकृत कर सकें।

3
Ch3steR 25 अगस्त 2021, 20:50

FWIW, Ch3steR का अच्छा जवाब पर एक छोटा सा अनुकूलन एक ग्रुपबाय करके और एक लेकर index कॉलम बनाना है प्रत्येक समूह की संचयी गणना, और फिर index_start द्वारा गणना की भरपाई करना:

rep = (df['index_end'] - df['index_start']) + 1
res = df.loc[df.index.repeat(rep)]
res['index'] = res.groupby(res.index).cumcount() + res['index_start']

आउटपुट (res):

  item  index_start  index_end  index
0    A            1          3      1
0    A            1          3      2
0    A            1          3      3
1    B            4          7      4
1    B            4          7      5
1    B            4          7      6
1    B            4          7      7

और समय, एक ला Ch3steR:

df = pd.concat([df]*1000, ignore_index=True)

%%timeit
rep = (df['index_end'] - df['index_start']) + 1
res = df.loc[df.index.repeat(rep)]
res['index'] = res.groupby(res.index).cumcount() + res['index_start']
# 1.66 ms ± 38.3 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

आपको विश्वास करना होगा कि अन्य दृष्टिकोणों के लिए मेरा समय पोस्ट किए गए के समान है:)


लेकिन मेरे लिए, यह एक इंडेक्स बनाने के लिए ओवरकिल जैसा लगता है। यदि आपकी मूल पंक्तियों में संदर्भित सूचकांक हमेशा सन्निहित और गैर-अतिव्यापी होते हैं (अर्थात यदि एक प्रविष्टि i से j तक फैली हुई है, तो अगली j + 1< से शुरू होती है। /em>), तो index केवल पूर्णांकों की एक श्रृंखला होने जा रहा है। तो इसे range के साथ बनाना सही होना चाहिए:

res['index'] = np.arange(1, len(df) + 1)

या शायद कार्यात्मक रूप से समान रूप से, आप केवल index बनाने के बजाय DataFrame की अनुक्रमणिका को रीसेट कर सकते हैं:

res = res.reset_index(drop=True)

आपके उदाहरण इनपुट से ऐसा लगता है कि यह पर्याप्त होगा। लेकिन काउंटर केस होगा यदि आपके index_start और index_end का अर्थ शाब्दिक पंक्ति संख्या नहीं है, और चीजों को दोहराने के तरीके की गणना करने के लिए अधिक उपयोग किया जाता है। तो उदाहरण के लिए, यदि आपका डेटा ऐसा दिखता है:

# now there are gaps in the indices, and it doesn't start at 1
df = pd.DataFrame({'item':['A','B'],
                   'index_start':[3, 5],
                   'index_end':[19, 22]})

लेकिन आप अभी भी चाहते थे कि पहली पंक्ति 3 बार और दूसरी 4 बार दोहराई जाए, और आप वह index कॉलम बनाना चाहते थे (इस मामले में, [3,4,5,19,20,21,22]), आपको ग्रुपबाय ऑपरेशन करने की आवश्यकता होगी या कुछ इसी तरह।

1
Tom 26 अगस्त 2021, 07:06