2016-09-21 20 views
7

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

# base: 
test <- read.table(
text = " 
1 A mnb USA prim 4 12 
2 A mnb USA x 13 15 
3 A mnb USA un 16 25 
4 A mnb USA fdfds 1 2 
5 B ghf CAN sdg 3 27 
6 B ghf CAN hgh 28 29 
7 B ghf CAN y 24 31 
8 B ghf CAN ghf 38 42 
",header=F) 
library(data.table) 
setDT(test) 
names(test) <- c("row","Person","Name","Country","add info","Begin","End") 
out <- read.table(
text = " 
1 A mnb USA fdfds 1 2 
2 A mnb USA - 4 25 
3 B ghf CAN - 3 31 
4 B ghf CAN ghf 38 42 
",header=F) 
setDT(out) 
names(out) <- c("row","Person","Name","Country","add info","Begin","End") 

समूह इस प्रकार से किया जाना चाहिए: व्यक्ति एक महीने 4 से लंबी पैदल यात्रा किया, तो 15 महीने तक और 16 महीने से 24 महीने तक यात्रा करते हुए, मैं लगातार 4 (या बिना ब्रेक के) गतिविधि 4 महीने से 24 महीने तक गतिविधि करता हूं। अगर बाद में व्यक्ति ए 25 से 28 महीने के महीने सर्फिंग करता है, तो मैं इसे भी जोड़ूंगा, और पूरी समूह गतिविधि 4 से 28 तक चली जाएगी। अब समस्याग्रस्त हैं मामले ओवरलैपिंग अवधि हैं, उदाहरण के लिए व्यक्ति ए 11 से 31 तक मछली पकड़ने भी कर सकता है, इसलिए पूरी चीज 4 से 31 हो जाएगी। हालांकि, अगर व्यक्ति ए 1 से 2 तक कुछ किया, यह एक अलग गतिविधि होगी (1 से 3 की तुलना में, जो भी होना चाहिए जोड़ा गया, क्योंकि 3 4 से जुड़ा हुआ है)। मुझे उम्मीद है कि यह स्पष्ट था, अगर नहीं, तो आप उपरोक्त कोड में और उदाहरण पा सकते हैं। मैं डेटाटेबल का उपयोग कर रहा हूं, क्योंकि मेरा डेटासेट काफी बड़ा है। मैंने अब तक sqldf के साथ शुरुआत की है, लेकिन यदि आपके पास प्रति व्यक्ति इतनी सारी गतिविधियां हैं (तो 8 या उससे अधिक कहें) यह समस्याग्रस्त है। क्या यह डेटाटेबल, या प्लीयर, या एसकल्डएफ में किया जा सकता है? कृपया ध्यान दें: मैं एसक्यूएल में एक उत्तर भी ढूंढ रहा हूं क्योंकि मैं इसे सीधे एसक्यूएलएफएफ में उपयोग कर सकता हूं या इसे किसी अन्य भाषा में बदलने की कोशिश करता हूं। sqldf समर्थन करता है (1) SQLite बैकएंड डेटाबेस (डिफ़ॉल्ट रूप से), (2) H2 जावा डेटाबेस, (3) PostgreSQL डेटाबेस और (4) sqldf 0.4-0 आगे भी MySQL का समर्थन करता है।

संपादित करें:

में:

Person Name Country add info Begin End 
A  mnb USA  prim  4  12 
A  mnb USA  x   13  15 
A  mnb USA  un  16  25 
A  mnb USA  fdfds  1  2 
B  ghf CAN  sdg  3  27 
B  ghf CAN  hgh  28  29 
B  ghf CAN  y   24  31 
B  ghf CAN  ghf  38  42 

आउट: यहाँ 'शुद्ध' टेबल हैं

A  mnb USA  fdfds  1  2 
A  mnb USA  -   4  25 
B  ghf CAN  -   3  31 
B  ghf CAN  ghf  38  42 
+0

इटज़िक बेन-गण द्वारा [पैकिंग अंतराल] (http://blogs.solidq.com/en/sqlserver/packing-intervals/) देखें। वह SQL सर्वर का उपयोग कर रहा है, लेकिन पोस्टग्रेज़ के नवीनतम संस्करण नवीनतम SQL सर्वर के रूप में समान विंडो फ़ंक्शन का समर्थन करते हैं, इसलिए पोस्टग्रेज़ में अपने SQL कोड को अनुकूलित करने के लिए यह छोटा होना चाहिए। विशेष रूप से, विंडो समग्र का उपयोग कर अंतिम समाधान 3 देखें। –

+0

टा एक आर समाधान के लिए – user3032689

+1

की जांच करेगा, [इस पोस्ट] (http://stackoverflow.com/questions/16957293/collapse-intersecting-regions-in-r) प्रासंगिक प्रतीत होता है। – Henrik

उत्तर

2

मैं इस एक है जो मेरे परीक्षण में काम किया था और लगभग सभी मुख्य डेटाबेस को सामान्य रूप से इसे चलाने चाहिए ... मैंने अपने कॉलम को रेखांकित किया है ... कृपया, परीक्षण से पहले नाम बदलें:

SELECT 
    r1.person_, 
    r1.name_, 
    r1.country_, 
    CASE 
    WHEN max(r2.begin_) = max(r1.begin_) 
    THEN max(r1.info_) ELSE '-' 
    END info_, 
    MAX(r2.begin_) begin_, 
    r1.end_ 
FROM stack_39626781 r1 
INNER JOIN stack_39626781 r2 ON 1=1 
    AND r2.person_ = r1.person_ 
    AND r2.begin_ <= r1.begin_ -- just optimizing... 
LEFT JOIN stack_39626781 r3 ON 1=1 
    AND r3.person_ = r1.person_ 
    -- matches when another range overlaps this range end 
    AND r3.end_ >= r1.end_ + 1 
    AND r3.begin_ <= r1.end_ + 1 
LEFT JOIN stack_39626781 r4 ON 1=1 
    AND r4.person_ = r2.person_ 
    -- matches when another range overlaps this range begin 
    AND r4.end_ >= r2.begin_ - 1 
    AND r4.begin_ <= r2.begin_ - 1 
WHERE 1=1 
    -- get rows 
    -- with no overlaps on end range and 
    -- with no overlaps on begin range 
    AND r3.person_ IS NULL 
    AND r4.person_ IS NULL 
GROUP BY 
    r1.person_, 
    r1.name_, 
    r1.country_, 
    r1.end_ 

यह क्वेरी इस तथ्य पर आधारित है कि आउटपुट से किसी भी सीमा में कोई कनेक्शन/ओवरलैप नहीं है।आइए कहें कि, पांच श्रेणियों के उत्पादन के लिए, पांच begin एस और पांच end एस कोई कनेक्शन/ओवरलैप नहीं है। उन्हें ढूंढें और संबद्ध करें सभी कनेक्शन/ओवरलैप उत्पन्न करने से आसान होना चाहिए। तो, यह क्वेरी क्या करती है:

  1. end मान पर कोई ओवरलैप/कनेक्शन वाले प्रति व्यक्ति की सभी श्रेणियां खोजें;
  2. begin मूल्य पर प्रति ओवरलैप/कनेक्शन वाले प्रति व्यक्ति की सभी श्रेणियां खोजें;
  3. ये मान्य श्रेणियां हैं, इसलिए उन्हें सही जोड़ी खोजने के लिए सभी को संबद्ध करें;
  4. प्रत्येक person और end के लिए, सही begin जोड़ी अधिकतम एक ही उपलब्ध है जो मूल्य के बराबर या इस end की तुलना में कम है ... यह इस नियम को मान्य करने के लिए आसान है ... पहले, आप की तुलना में एक अधिक से अधिक begin नहीं हो सकता end ... यदि आपके पास दो या दो से अधिक संभव beginend से कम है, तो ई। । जी, begin1 = अंत - 2 और begin2 = अंत - 5, का चयन करने के लिए कम एक ( begin2) अधिक से अधिक एक ( begin1) इस रेंज के एक जैसी होने पर बनाता है।

उम्मीद है कि यह मदद करता है।

+0

धन्यवाद। बस अपना कोड देखकर, मुझे आश्चर्य है कि यह 4 से अधिक लोगों के लिए भी काम करता है? क्योंकि मेरे डेटासेट में 100 व्यक्ति हो सकते हैं। इसके अलावा आप इसके लिए एसक्यूएल बनाम किस प्रकार इस्तेमाल करते थे? बस यह सुनिश्चित करने की ज़रूरत है कि मैं कोड लागू कर सकता हूं। – user3032689

+0

यह किसी भी व्यक्ति के लिए काम करता है और SQLite, MySQL, Postgre, Oracle, MariaDB पर काम करेगा ... आप इसे http://sqlfiddle.com पर देख सकते हैं –

+0

अतिरिक्त स्पष्टीकरण के लिए धन्यवाद। मैं कल इसका परीक्षण करूंगा। – user3032689

2

आप ऊपर एसक्यूएल सर्वर 2012 या साथ काम कर रहे हैं, तो आप उपयोग कर सकते हैं एलएजी और लीड फ़ंक्शंस आपके अंतिम वांछित डेटासेट पर पहुंचने के लिए अपना तर्क बनाने के लिए कार्य करता है। ओरेकल 8i के बाद से ये कार्य ओरेकल में भी उपलब्ध हैं, मेरा मानना ​​है।

नीचे एक समाधान है जिसे मैंने SQL Server 2012 में बनाया है, जो आपकी मदद करनी चाहिए। आपके द्वारा प्रदान किए गए उदाहरण मान एक अस्थायी तालिका में लोड किए गए हैं जिन्हें आप अपनी पहली "शुद्ध तालिका" के रूप में संदर्भित करते हैं। उन दो कार्यों का उपयोग करते हुए, ओवर क्लॉज के साथ, मैं नीचे दिए गए निम्नलिखित टी-एसक्यूएल कोड के साथ आपके अंतिम डेटासेट पर पहुंचा। मैंने कोड में कुछ टिप्पणी लाइनों को छोड़ दिया ताकि यह दिखाया जा सके कि मैं समग्र समाधान टुकड़ा-टुकड़ा कैसे बना सकता हूं, जो गैपमार्कर कॉलम के लिए केस स्टेटमेंट में रखे गए विभिन्न परिदृश्यों के लिए जिम्मेदार है जो समूह ध्वज के रूप में कार्य करता है।

IF OBJECT_ID('tempdb..#MyTable') IS NOT NULL 
 
\t DROP TABLE #MyTable 
 

 
CREATE TABLE #MyTable (
 
\t Person CHAR(1) 
 
\t ,[Name] VARCHAR(3) 
 
\t ,Country VARCHAR(10) 
 
\t ,add_info VARCHAR(10) 
 
\t ,[Begin] INT 
 
\t ,[End] INT 
 
) 
 

 
INSERT INTO #MyTable (Person, Name, Country, add_info, [Begin], [End]) 
 
\t VALUES ('A', 'mnb', 'USA', 'prim', 4, 12), 
 
\t ('A', 'mnb', 'USA', 'x', 13, 15), 
 
\t ('A', 'mnb', 'USA', 'un', 16, 25), 
 
\t ('A', 'mnb', 'USA', 'fdfds', 1, 2), 
 
\t ('B', 'ghf', 'CAN', 'sdg', 3, 27), 
 
\t ('B', 'ghf', 'CAN', 'hgh', 28, 29), 
 
\t ('B', 'ghf', 'CAN', 'y', 24, 31), 
 
\t ('B', 'ghf', 'CAN', 'ghf', 38, 42); 
 

 
WITH CTE 
 
AS 
 
(SELECT 
 
\t \t mt.Person 
 
\t \t ,mt.Name 
 
\t \t ,mt.Country 
 
\t \t ,mt.add_info 
 
\t \t ,mt.[Begin] 
 
\t \t ,mt.[End] 
 
\t \t --,LEAD([Begin], 1) OVER (PARTITION BY mt.Person ORDER BY [End]) 
 
\t \t --,CASE WHEN [End] + 1 = LEAD([Begin], 1) OVER (PARTITION BY mt.Person ORDER BY [End]) 
 
\t \t --  --AND LEAD([Begin], 1) OVER (PARTITION BY mt.Person ORDER BY [End]) = LEAD([End], 1) OVER (PARTITION BY mt.Person ORDER BY [End]) 
 
\t \t -- \t THEN 1 
 
\t \t -- \t ELSE 0 
 
\t \t -- END AS Grp 
 
\t \t --,MARKER = COALESCE(LEAD([Begin], 1) OVER (PARTITION BY mt.Person ORDER BY [End]), LAG([End], 1) OVER (PARTITION BY mt.Person ORDER BY [End])) 
 
\t \t ,CASE 
 
\t \t \t WHEN mt.[End] + 1 = COALESCE(LEAD([Begin], 1) OVER (PARTITION BY mt.Person ORDER BY [End]), LAG([End], 1) OVER (PARTITION BY mt.Person ORDER BY [End])) OR 
 
\t \t \t \t 1 + COALESCE(LEAD([Begin], 1) OVER (PARTITION BY mt.Person ORDER BY [End]), LAG([End], 1) OVER (PARTITION BY mt.Person ORDER BY [End])) = mt.[Begin] OR 
 
\t \t \t \t COALESCE(LEAD([Begin], 1) OVER (PARTITION BY mt.Person ORDER BY [Begin]), LAG([End], 1) OVER (PARTITION BY mt.Person ORDER BY [Begin])) BETWEEN mt.[Begin] AND mt.[End] OR 
 
\t \t \t \t [End] BETWEEN LAG([Begin], 1) OVER (PARTITION BY mt.Person ORDER BY [Begin]) AND LAG([End], 1) OVER (PARTITION BY mt.Person ORDER BY [Begin]) THEN 1 
 
\t \t \t ELSE 0 
 
\t \t END AS GapMarker 
 
\t \t ,InBetween = COALESCE(LEAD([Begin], 1) OVER (PARTITION BY mt.Person ORDER BY [Begin]), LAG([End], 1) OVER (PARTITION BY mt.Person ORDER BY [Begin])) 
 
\t \t ,EndInBtw = LAG([Begin], 1) OVER (PARTITION BY mt.Person ORDER BY [Begin]) 
 
\t \t ,LagEndInBtw = LAG([End], 1) OVER (PARTITION BY mt.Person ORDER BY [Begin]) 
 
\t FROM #MyTable mt 
 
--ORDER BY mt.Person, mt.[Begin] 
 
) 
 
SELECT DISTINCT 
 
\t X.Person 
 
\t ,X.[Name] 
 
\t ,X.Country 
 
\t ,t.add_info 
 
\t ,X.MinBegin 
 
\t ,X.MaxEnd 
 
FROM (SELECT 
 
\t \t c.Person 
 
\t \t ,c.[Name] 
 
\t \t ,c.Country 
 
\t \t ,c.add_info 
 
\t \t ,c.[Begin] 
 
\t \t ,c.[End] 
 
\t \t ,c.GapMarker 
 
\t \t ,c.InBetween 
 
\t \t ,c.EndInBtw 
 
\t \t ,c.LagEndInBtw 
 
\t \t ,MIN(c.[Begin]) OVER (PARTITION BY c.Person, c.GapMarker ORDER BY c.Person) AS MinBegin 
 
\t \t ,MAX(c.[End]) OVER (PARTITION BY c.Person, c.GapMarker ORDER BY c.Person) AS MaxEnd 
 
\t --, CASE WHEN c.[End]+1 = c.MARKER 
 
\t --  OR c.MARKER +1 = c.[Begin] 
 
\t -- THEN 1 
 
\t -- ELSE 0 
 
\t -- END Grp 
 
\t FROM CTE AS c) X 
 
LEFT JOIN #MyTable AS t 
 
\t ON t.[Begin] = X.[MinBegin] 
 
\t \t AND t.[End] = X.[MaxEnd] 
 
\t \t AND t.Person = X.Person 
 
ORDER BY X.Person, X.MinBegin 
 
--ORDER BY Person, [Begin]

और यहाँ परिणाम है कि अपने वांछित अंतिम डाटासेट से मेल का एक स्क्रीन शॉट है:

enter image description here

+0

Ty, यह बहुत अच्छा लग रहा है, लेकिन दुर्भाग्य से मैं इसका परीक्षण नहीं कर सका। मेरे पास केवल आर के लिए 'sqldf' पैकेज है, जो कहता है: sqldf समर्थन करता है (1) SQLite बैकएंड डेटाबेस (डिफ़ॉल्ट रूप से), (2) H2 जावा डेटाबेस, (3) PostgreSQL डेटाबेस और (4) sqldf 0.4-0 आगे भी MySQL का समर्थन करता है। मुझे यकीन नहीं है कि अब इस SQL ​​सर्वर कोड को निष्पादित करने के लिए कैसे, लेकिन अच्छी तरह से। तो अब मुझे सिंटैक्स का अनुवाद कुछ SQL संस्करण में करना होगा जो विंडोिंग का समर्थन करता है, सही? – user3032689

+1

@ user3032689, पोस्टग्रेज़ के नवीनतम संस्करण SQL सर्वर 2012+ के समान विंडो फ़ंक्शन का समर्थन करते हैं। –

+0

ठीक है वे करते हैं, लेकिन यह अभी भी PostgresSQL के समान वाक्यविन्यास नहीं है? तो अंत में मुझे लगता है कि दोनों भाषाओं के बीच अनुवाद करना होगा। – user3032689

संबंधित मुद्दे