python - अंतर को हाइलाइट करते हुए-दो पांडों डेटाफ्रेम में तरफ से आउटपुटिंग अंतर




html pandas (7)

दो डेटाफ्रेम के बीच अंतर को हाइलाइट करना

एक अंतर है जहां कोशिकाओं के पृष्ठभूमि रंग को हाइलाइट करने के लिए डेटाफ्रेम शैली संपत्ति का उपयोग करना संभव है।

मूल प्रश्न से उदाहरण डेटा का उपयोग करना

पहला चरण डेटाफ्रेम को क्षैतिज रूप से संगत फ़ंक्शन के साथ जोड़ना है और प्रत्येक फ्रेम को keys पैरामीटर के साथ अलग करना है:

df_all = pd.concat([df.set_index('id'), df2.set_index('id')], 
                   axis='columns', keys=['First', 'Second'])
df_all

कॉलम स्तर को स्वैप करना और एक ही कॉलम नाम एक-दूसरे के बगल में रखना संभव है:

df_final = df_all.swaplevel(axis='columns')[df.columns[1:]]
df_final

अब, फ्रेम में मतभेदों को खोजना बहुत आसान है। लेकिन, हम अलग-अलग कोशिकाओं को हाइलाइट करने के लिए आगे बढ़ सकते हैं और style प्रॉपर्टी का उपयोग कर सकते हैं। हम ऐसा करने के लिए एक कस्टम फ़ंक्शन को परिभाषित करते हैं जिसे आप दस्तावेज़ के इस भाग में देख सकते हैं।

def highlight_diff(data, color='yellow'):
    attr = 'background-color: {}'.format(color)
    other = data.xs('First', axis='columns', level=-1)
    return pd.DataFrame(np.where(data.ne(other, level=0), attr, ''),
                        index=data.index, columns=data.columns)

df_final.style.apply(highlight_diff, axis=None)

यह उन कोशिकाओं को हाइलाइट करेगा जो दोनों में मूल्य गुम हैं। आप या तो उन्हें भर सकते हैं या अतिरिक्त तर्क प्रदान कर सकते हैं ताकि वे हाइलाइट न हों।

मैं दो डेटाफ्रेम के बीच क्या बदल गया है, इसे हाइलाइट करने की कोशिश कर रहा हूं।

मान लीजिए मेरे पास दो पायथन पांडस डेटाफ्रेम हैं:

"StudentRoster Jan-1":
id   Name   score                    isEnrolled           Comment
111  Jack   2.17                     True                 He was late to class
112  Nick   1.11                     False                Graduated
113  Zoe    4.12                     True       

"StudentRoster Jan-2":
id   Name   score                    isEnrolled           Comment
111  Jack   2.17                     True                 He was late to class
112  Nick   1.21                     False                Graduated
113  Zoe    4.12                     False                On vacation

मेरा लक्ष्य एक HTML तालिका आउटपुट करना है जो:

  1. पंक्तियों की पहचान करता है जो बदल गए हैं (int, float, बूलियन, स्ट्रिंग हो सकता है)
  2. आउटपुट पंक्तियां समान, पुरानी और नई मानों (आदर्श रूप से एक HTML तालिका में) के साथ पंक्तियां होती हैं ताकि उपभोक्ता स्पष्ट रूप से देख सकें कि दो डेटाफ्रेम के बीच क्या बदल गया है:

    "StudentRoster Difference Jan-1 - Jan-2":  
    id   Name   score                    isEnrolled           Comment
    112  Nick   was 1.11| now 1.21       False                Graduated
    113  Zoe    4.12                     was True | now False was "" | now   "On   vacation"
    

मुझे लगता है कि मैं कॉलम तुलना द्वारा पंक्ति और कॉलम द्वारा एक पंक्ति कर सकता हूं, लेकिन क्या कोई आसान तरीका है?


@ जर्नोइस के जवाब के साथ झुकाव के बाद, मैं पैनल के विकृति के कारण पैनल के बजाय मल्टीइंडेक्स का उपयोग करके इसे काम करने में सक्षम था।

सबसे पहले, कुछ डमी डेटा बनाएं:

df1 = pd.DataFrame({
    'id': ['111', '222', '333', '444', '555'],
    'let': ['a', 'b', 'c', 'd', 'e'],
    'num': ['1', '2', '3', '4', '5']
})
df2 = pd.DataFrame({
    'id': ['111', '222', '333', '444', '666'],
    'let': ['a', 'b', 'c', 'D', 'f'],
    'num': ['1', '2', 'Three', '4', '6'],
})

फिर, अपने diff फ़ंक्शन को परिभाषित करें, इस मामले में मैं उसके उत्तर से उसका उपयोग report_diff वही रहता है:

def report_diff(x):
    return x[0] if x[0] == x[1] else '{} | {}'.format(*x)

फिर, मैं डेटा को मल्टीइंडेक्स डेटाफ्रेम में जोड़ना चाहता हूं:

df_all = pd.concat(
    [df1.set_index('id'), df2.set_index('id')], 
    axis='columns', 
    keys=['df1', 'df2'],
    join='outer'
)
df_all = df_all.swaplevel(axis='columns')[df1.columns[1:]]

और अंत में मैं प्रत्येक कॉलम समूह के नीचे report_diff लागू करने जा रहा हूं:

df_final.groupby(level=0, axis=1).apply(lambda frame: frame.apply(report_diff, axis=1))

यह आउटपुट:

         let        num
111        a          1
222        b          2
333        c  3 | Three
444    d | D          4
555  e | nan    5 | nan
666  nan | f    nan | 6

इतना ही!


Concat और drop_duplicates का उपयोग करके एक अलग दृष्टिकोण:

import sys
if sys.version_info[0] < 3:
    from StringIO import StringIO
else:
    from io import StringIO
import pandas as pd

DF1 = StringIO("""id   Name   score                    isEnrolled           Comment
111  Jack   2.17                     True                 "He was late to class"
112  Nick   1.11                     False                "Graduated"
113  Zoe    NaN                     True                  " "
""")
DF2 = StringIO("""id   Name   score                    isEnrolled           Comment
111  Jack   2.17                     True                 "He was late to class"
112  Nick   1.21                     False                "Graduated"
113  Zoe    NaN                     False                "On vacation" """)

df1 = pd.read_table(DF1, sep='\s+', index_col='id')
df2 = pd.read_table(DF2, sep='\s+', index_col='id')
#%%
dictionary = {1:df1,2:df2}
df=pd.concat(dictionary)
df.drop_duplicates(keep=False)

आउटपुट:

       Name  score isEnrolled      Comment
  id                                      
1 112  Nick   1.11      False    Graduated
  113   Zoe    NaN       True             
2 112  Nick   1.21      False    Graduated
  113   Zoe    NaN      False  On vacation

चयन और विलय का उपयोग करके एक और तरीका यहां दिया गया है:

In [6]: # first lets create some dummy dataframes with some column(s) different
   ...: df1 = pd.DataFrame({'a': range(-5,0), 'b': range(10,15), 'c': range(20,25)})
   ...: df2 = pd.DataFrame({'a': range(-5,0), 'b': range(10,15), 'c': [20] + list(range(101,105))})


In [7]: df1
Out[7]:
   a   b   c
0 -5  10  20
1 -4  11  21
2 -3  12  22
3 -2  13  23
4 -1  14  24


In [8]: df2
Out[8]:
   a   b    c
0 -5  10   20
1 -4  11  101
2 -3  12  102
3 -2  13  103
4 -1  14  104


In [10]: # make condition over the columns you want to comapre
    ...: condition = df1['c'] != df2['c']
    ...:
    ...: # select rows from each dataframe where the condition holds
    ...: diff1 = df1[condition]
    ...: diff2 = df2[condition]


In [11]: # merge the selected rows (dataframes) with some suffixes (optional)
    ...: diff1.merge(diff2, on=['a','b'], suffixes=('_before', '_after'))
Out[11]:
   a   b  c_before  c_after
0 -4  11        21      101
1 -3  12        22      102
2 -2  13        23      103
3 -1  14        24      104

जुपीटर स्क्रीनशॉट से वही बात है:


मुझे इस मुद्दे का सामना करना पड़ा है, लेकिन इस पोस्ट को ढूंढने से पहले एक जवाब मिला:

Unutbu के उत्तर के आधार पर, अपना डेटा लोड करें ...

import pandas as pd
import io

texts = ['''\
id   Name   score                    isEnrolled                       Date
111  Jack                            True              2013-05-01 12:00:00
112  Nick   1.11                     False             2013-05-12 15:05:23
     Zoe    4.12                     True                                  ''',

         '''\
id   Name   score                    isEnrolled                       Date
111  Jack   2.17                     True              2013-05-01 12:00:00
112  Nick   1.21                     False                                
     Zoe    4.12                     False             2013-05-01 12:00:00''']


df1 = pd.read_fwf(io.BytesIO(texts[0]), widths=[5,7,25,17,20], parse_dates=[4])
df2 = pd.read_fwf(io.BytesIO(texts[1]), widths=[5,7,25,17,20], parse_dates=[4])

... अपने diff समारोह को परिभाषित करें ...

def report_diff(x):
    return x[0] if x[0] == x[1] else '{} | {}'.format(*x)

फिर आप निष्कर्ष निकालने के लिए बस एक पैनल का उपयोग कर सकते हैं:

my_panel = pd.Panel(dict(df1=df1,df2=df2))
print my_panel.apply(report_diff, axis=0)

#          id  Name        score    isEnrolled                       Date
#0        111  Jack   nan | 2.17          True        2013-05-01 12:00:00
#1        112  Nick  1.11 | 1.21         False  2013-05-12 15:05:23 | NaT
#2  nan | nan   Zoe         4.12  True | False  NaT | 2013-05-01 12:00:00

वैसे, यदि आप आईपीथॉन नोटबुक में हैं, तो आप रंग देने के लिए रंगीन डिफ फ़ंक्शन का उपयोग करना चाह सकते हैं, भले ही कोशिकाएं अलग हों, बराबर या बाएं / दाएं नल:

from IPython.display import HTML
pd.options.display.max_colwidth = 500  # You need this, otherwise pandas
#                          will limit your HTML strings to 50 characters

def report_diff(x):
    if x[0]==x[1]:
        return unicode(x[0].__str__())
    elif pd.isnull(x[0]) and pd.isnull(x[1]):
        return u'<table style="background-color:#00ff00;font-weight:bold;">'+\
            '<tr><td>%s</td></tr><tr><td>%s</td></tr></table>' % ('nan', 'nan')
    elif pd.isnull(x[0]) and ~pd.isnull(x[1]):
        return u'<table style="background-color:#ffff00;font-weight:bold;">'+\
            '<tr><td>%s</td></tr><tr><td>%s</td></tr></table>' % ('nan', x[1])
    elif ~pd.isnull(x[0]) and pd.isnull(x[1]):
        return u'<table style="background-color:#0000ff;font-weight:bold;">'+\
            '<tr><td>%s</td></tr><tr><td>%s</td></tr></table>' % (x[0],'nan')
    else:
        return u'<table style="background-color:#ff0000;font-weight:bold;">'+\
            '<tr><td>%s</td></tr><tr><td>%s</td></tr></table>' % (x[0], x[1])

HTML(my_panel.apply(report_diff, axis=0).to_html(escape=False))

यदि आपके दो डेटाफ्रेम में समान आईडी हैं, तो पता लगाना कि क्या बदल गया है वास्तव में बहुत आसान है। बस फ्रेम 1 कर रहा है frame1 != frame2 फ्रेम frame1 != frame2 आपको एक बुलियन डेटाफ्रेम देगा जहां प्रत्येक True डेटा बदल गया है। उस से, आप आसानी से बदलते हुए प्रत्येक changedids = frame1.index[np.any(frame1 != frame2,axis=1)] की अनुक्रमणिका को बदल changedids = frame1.index[np.any(frame1 != frame2,axis=1)]


import pandas as pd
import io

texts = ['''\
id   Name   score                    isEnrolled                        Comment
111  Jack   2.17                     True                 He was late to class
112  Nick   1.11                     False                           Graduated
113  Zoe    4.12                     True       ''',

         '''\
id   Name   score                    isEnrolled                        Comment
111  Jack   2.17                     True                 He was late to class
112  Nick   1.21                     False                           Graduated
113  Zoe    4.12                     False                         On vacation''']


df1 = pd.read_fwf(io.BytesIO(texts[0]), widths=[5,7,25,21,20])
df2 = pd.read_fwf(io.BytesIO(texts[1]), widths=[5,7,25,21,20])
df = pd.concat([df1,df2]) 

print(df)
#     id  Name  score isEnrolled               Comment
# 0  111  Jack   2.17       True  He was late to class
# 1  112  Nick   1.11      False             Graduated
# 2  113   Zoe   4.12       True                   NaN
# 0  111  Jack   2.17       True  He was late to class
# 1  112  Nick   1.21      False             Graduated
# 2  113   Zoe   4.12      False           On vacation

df.set_index(['id', 'Name'], inplace=True)
print(df)
#           score isEnrolled               Comment
# id  Name                                        
# 111 Jack   2.17       True  He was late to class
# 112 Nick   1.11      False             Graduated
# 113 Zoe    4.12       True                   NaN
# 111 Jack   2.17       True  He was late to class
# 112 Nick   1.21      False             Graduated
# 113 Zoe    4.12      False           On vacation

def report_diff(x):
    return x[0] if x[0] == x[1] else '{} | {}'.format(*x)

changes = df.groupby(level=['id', 'Name']).agg(report_diff)
print(changes)

प्रिंट

                score    isEnrolled               Comment
id  Name                                                 
111 Jack         2.17          True  He was late to class
112 Nick  1.11 | 1.21         False             Graduated
113 Zoe          4.12  True | False     nan | On vacation






panel