मैं पांडा में निम्नलिखित डेटा को संसाधित करने के लिए एक कुशल तरीका ढूंढ रहा हूं।

मेरे पास एक डेटाफ़्रेम है जिसमें कई सौ हज़ार प्रारंभ और समाप्ति टाइमस्टैम्प हैं:

data_df
                      start_ts                     end_ts
0    2019-06-10 12:00:00+00:00  2019-06-10 22:30:00+00:00
1    2019-06-11 12:00:00+00:00  2019-06-11 13:30:00+00:00
2    2019-06-11 14:00:00+00:00  2019-06-11 19:00:00+00:00
3    2019-06-14 12:00:00+00:00  2019-06-14 18:30:00+00:00
4    2019-06-10 12:00:00+00:00  2019-06-10 21:30:00+00:00
5    2019-06-11 12:00:00+00:00  2019-06-11 18:30:00+00:00
...

मेरे पास लेबल किए गए टाइम बिन्स (tp1-tp10) का एक सेट भी है। प्रत्येक दिन १० डिब्बे होते हैं, लेकिन उन डिब्बे का समय दिन-प्रतिदिन बदल सकता है (जैसे- tp1 एक दिन में ००:०० से ०१:३० तक हो सकता है, लेकिन फिर ००:०० से ०१:४५ तक किसी और दिन)। संसाधित किए जाने वाले प्रत्येक डेटा सेट में ७ दिन होते हैं, प्रति दिन १० समयावधियां होती हैं, इसलिए श्रेणियों का सेट आकार ७० का होता है, और ऐसा दिखता है:

labeled_bins_df
                   start_range                  end_range  label
0    2019-06-10 00:00:00+00:00  2019-06-10 04:30:00+00:00    tp1
1    2019-06-10 04:30:00+00:00  2019-06-10 09:45:00+00:00    tp2
2    2019-06-10 09:45:00+00:00  2019-06-10 12:30:00+00:00    tp3
...

मैं जो चाहता हूं वह मूल data_df डेटा के साथ एक तालिका है, लेकिन अतिरिक्त कॉलम के साथ, tp1 से tp10, प्रत्येक पंक्ति में मिनटों की संख्या के साथ:

timed_bins
                      start_ts                     end_ts    tp1    tp2    tp3    tp4 ...
0    2019-06-10 12:00:00+00:00  2019-06-10 22:30:00+00:00      0      0     30    120 ...
1    2019-06-11 12:00:00+00:00  2019-06-11 13:30:00+00:00      0     45     45      0 ...

मैं वर्तमान में यह भोलेपन से कर रहा हूं, अपनी पंक्तियों पर लूपिंग कर रहा हूं, और प्रत्येक डेटा पंक्ति में डिब्बे की खोज कर रहा हूं, और जैसा कि आप कल्पना कर सकते हैं, यह काफी धीमा है। क्या कोई पांडा-फू है जिसे डेटाटाइम सीमाओं पर इस तरह की बिनिंग करने के लिए किया जा सकता है?

संपादित करें: एक विचार, जो एक नई दिशा में सोचने में मदद कर सकता है। अगर मैं अपने सभी टाइमस्टैम्प (मेरे डेटा में, और मेरे लेबल वाले डिब्बे में) को यूनिक्स टाइमस्टैम्प (1 जनवरी, 1970 के बाद से सेकंड) में बदल देता हूं, तो यह तारीखों के बजाय पूर्णांक श्रेणियों के आधार पर बिनिंग / योग का मामला होगा। . यह तब प्रत्येक बिन में सेकंड की संख्या देगा, बस 60 से विभाजित होगा, और मुझे प्रत्येक बिन में मेरे मिनट मिलेंगे। यह तिथि सीमाओं आदि के बारे में सभी चिंताओं को दूर करता है।

संपादित करें 2: अनुरोध के अनुसार, यहां तीन अलग-अलग समय डिब्बे का उपयोग करके सरलीकृत नमूना डेटा का एक सेट दिया गया है। मैंने विशेष रूप से डेटा नमूनों में से एक (दूसरी पंक्ति) 2 दिनों की अवधि में बनाया है। इसके अतिरिक्त, एक result_df है जो अपेक्षित आउटपुट दिखाता है।

data_samples = [
    {'start_ts': '2019-06-10T12:00:00+0000', 'end_ts': '2019-06-10T22:30:00+0000'},
    {'start_ts': '2019-06-10T22:00:00+0000', 'end_ts': '2019-06-11T05:30:00+0000'},
    {'start_ts': '2019-06-10T10:00:00+0000', 'end_ts': '2019-06-10T14:15:00+0000'},
    {'start_ts': '2019-06-12T08:07:00+0000', 'end_ts': '2019-06-12T18:22:00+0000'},
    {'start_ts': '2019-06-11T14:03:00+0000', 'end_ts': '2019-06-11T15:30:00+0000'},
    {'start_ts': '2019-06-11T02:33:00+0000', 'end_ts': '2019-06-11T10:31:00+0000'}
]

data_set = [{
    'start_ts': datetime.datetime.strptime(x['start_ts'], '%Y-%m-%dT%H:%M:%S%z'),
    'end_ts': datetime.datetime.strptime(x['end_ts'], '%Y-%m-%dT%H:%M:%S%z')} for x in data_samples]

data_df = pd.DataFrame(data_set)[['start_ts', 'end_ts']]

time_bin_samples = [
    {'start_ts': '2019-06-10T00:00:00+0000', 'end_ts': '2019-06-10T08:15:00+0000', 'label': 't1'},
    {'start_ts': '2019-06-10T08:15:00+0000', 'end_ts': '2019-06-10T18:00:00+0000', 'label': 't2'},
    {'start_ts': '2019-06-10T18:00:00+0000', 'end_ts': '2019-06-11T00:00:00+0000', 'label': 't3'},

    {'start_ts': '2019-06-11T00:00:00+0000', 'end_ts': '2019-06-11T09:00:00+0000', 'label': 't1'},
    {'start_ts': '2019-06-11T09:00:00+0000', 'end_ts': '2019-06-11T19:15:00+0000', 'label': 't2'},
    {'start_ts': '2019-06-11T19:15:00+0000', 'end_ts': '2019-06-12T00:00:00+0000', 'label': 't3'},

    {'start_ts': '2019-06-12T00:00:00+0000', 'end_ts': '2019-06-12T10:30:00+0000', 'label': 't1'},
    {'start_ts': '2019-06-12T10:30:00+0000', 'end_ts': '2019-06-12T12:00:00+0000', 'label': 't2'},
    {'start_ts': '2019-06-12T12:00:00+0000', 'end_ts': '2019-06-13T00:00:00+0000', 'label': 't3'},
]

time_bin_set = [{
    'start_ts': datetime.datetime.strptime(x['start_ts'], '%Y-%m-%dT%H:%M:%S%z'),
    'end_ts': datetime.datetime.strptime(x['end_ts'], '%Y-%m-%dT%H:%M:%S%z'),
    'label': x['label']} for x in time_bin_samples
]

time_bin_df = pd.DataFrame(time_bin_set)[['start_ts', 'end_ts', 'label']]

result_set = [
    {'t1': 0, 't2': 360, 't3': 270},
    {'t1': 330, 't2': 0, 't3': 120},
    {'t1': 0, 't2': 255, 't3': 0},
    {'t1': 143, 't2': 90, 't3': 382},
    {'t1': 0, 't2': 87, 't3': 0},
    {'t1': 387, 't2': 91, 't3': 0}
]

result_df = pd.DataFrame(result_set)
3
MarkD 6 जुलाई 2019, 18:02

1 उत्तर

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

मुझे पता है कि डेटाफ्रेम की पंक्तियों को पुनरावृत्त करना कुशल नहीं है।

यहां मैं merge_asof का उपयोग करके data_df में प्रति पंक्ति पहले और अंतिम बिन की पहचान करने का प्रयास करूंगा।

फिर मैं एक पंक्ति के अनुरूप सभी डिब्बे जोड़ने के लिए डेटाफ्रेम मानों को एक बार पुनरावृत्त करके उप-डेटाफ्रेम की एक सूची तैयार करूंगा और उस सूची को जोड़ दूंगा।

वहां से यह प्रति बिन समय अंतराल की गणना करने और अपेक्षित परिणाम प्राप्त करने के लिए pivot_table का उपयोग करने के लिए पर्याप्त है।

कोड हो सकता है:

# store the index as a column to make sure to keep it
data_df = data_df.rename_axis('ix').reset_index().sort_values(
    ['end_ts', 'start_ts'])
time_bin_df = time_bin_df.rename_axis('ix').reset_index().sort_values(
    ['end_ts', 'start_ts'])

# identify first and last bin per row
first = pd.merge_asof(data_df, time_bin_df, left_on='start_ts',
                      right_on='end_ts', suffixes=('', '_first'),
                      direction='forward').values
last = pd.merge_asof(data_df, time_bin_df, left_on='end_ts', right_on='start_ts',
                     suffixes=('', '_ bin')).values

# build a list of bin dataframes (one per row in data_df)
data = []
for i, val in enumerate(first):
    elt = time_bin_df[(time_bin_df['ix']>=val[3])
                      &(time_bin_df['ix']<=last[i][3])].copy()
    # compute the begin and end of the intersection of the period and the bin
    elt.loc[elt['start_ts']<val[1], 'start_ts'] = val[1]
    elt.loc[elt['end_ts']>val[2], 'end_ts'] = val[2]
    elt['ix_data'] = val[0]
    data.append(elt)

# concat everything
tmp = pd.concat(data)

# compute durations in minutes
tmp['duration'] = (tmp['end_ts'] - tmp['start_ts']).dt.total_seconds() / 60

# pivot to get the expected result
result_df = tmp.pivot_table('duration', 'ix_data', 'label', 'sum', fill_value=0
                            ).rename_axis(None).rename_axis(None, axis=1)

इसमें कुछ समय लग सकता है क्योंकि डेटाफ्रेम की सूची बनाने के लिए अभी भी एक लंबा ऑपरेशन है, लेकिन अन्य ऑपरेशन वेक्टरकृत होने चाहिए।

0
Serge Ballesta 7 जुलाई 2019, 01:00