두 개의 DataFrame을 비교하고 차이점을 나란히 출력하십시오. Zoe

두 데이터 프레임 사이에서 변경된 내용을 정확하게 강조하려고합니다.

두 개의 Python Pandas 데이터 프레임이 있다고 가정합니다.

"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, boolean, string 일 수 있음)
  2. 소비자는 동일한 OLD 및 NEW 값 (이상적으로 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"

행 단위로, 열 단위로 열을 비교할 수 있다고 생각하지만 더 쉬운 방법이 있습니까?



답변

첫 번째 부분은 Constantine과 비슷하며 어떤 행이 비어있는 부울을 얻을 수 있습니다.

In [21]: ne = (df1 != df2).any(1)

In [22]: ne
Out[22]:
0    False
1     True
2     True
dtype: bool

그러면 어떤 항목이 변경되었는지 확인할 수 있습니다.

In [23]: ne_stacked = (df1 != df2).stack()

In [24]: changed = ne_stacked[ne_stacked]

In [25]: changed.index.names = ['id', 'col']

In [26]: changed
Out[26]:
id  col
1   score         True
2   isEnrolled    True
    Comment       True
dtype: bool

여기서 첫 번째 항목은 색인이고 두 번째 항목은 변경된 열입니다.

In [27]: difference_locations = np.where(df1 != df2)

In [28]: changed_from = df1.values[difference_locations]

In [29]: changed_to = df2.values[difference_locations]

In [30]: pd.DataFrame({'from': changed_from, 'to': changed_to}, index=changed.index)
Out[30]:
               from           to
id col
1  score       1.11         1.21
2  isEnrolled  True        False
   Comment     None  On vacation

* 참고 : 여기서 동일한 인덱스를 공유 df1하고 df2공유하는 것이 중요합니다 . 이 모호성을 극복하기 위해을 사용하여 공유 레이블 만 볼 수는 df1.index & df2.index있지만 연습으로 남겨 두겠다고 생각합니다.


답변

두 DataFrame의 차이점 강조

DataFrame 스타일 속성을 사용하여 차이가있는 셀의 배경색을 강조 표시 할 수 있습니다.

원래 질문의 예제 데이터 사용

첫 번째 단계는 concat함수와 DataFrame을 가로로 연결 하고 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)

여기에 이미지 설명을 입력하십시오

누락 된 값이있는 셀이 강조 표시됩니다. 강조 표시되지 않도록 채우기 또는 추가 논리를 제공 할 수 있습니다.


답변

이 답변은 단순히 @Andy Hayden ‘s를 확장하여 숫자 필드가 nan인 경우 탄력적이며 함수로 묶 습니다.

import pandas as pd
import numpy as np


def diff_pd(df1, df2):
    """Identify differences between two pandas DataFrames"""
    assert (df1.columns == df2.columns).all(), \
        "DataFrame column names are different"
    if any(df1.dtypes != df2.dtypes):
        "Data Types are different, trying to convert"
        df2 = df2.astype(df1.dtypes)
    if df1.equals(df2):
        return None
    else:
        # need to account for np.nan != np.nan returning True
        diff_mask = (df1 != df2) & ~(df1.isnull() & df2.isnull())
        ne_stacked = diff_mask.stack()
        changed = ne_stacked[ne_stacked]
        changed.index.names = ['id', 'col']
        difference_locations = np.where(diff_mask)
        changed_from = df1.values[difference_locations]
        changed_to = df2.values[difference_locations]
        return pd.DataFrame({'from': changed_from, 'to': changed_to},
                            index=changed.index)

따라서 데이터 (점수 열에 NaN을 갖도록 약간 편집 됨)를 사용하십시오.

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

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')
diff_pd(df1, df2)

산출:

                from           to
id  col
112 score       1.11         1.21
113 isEnrolled  True        False
    Comment           On vacation


답변

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.StringIO(texts[0]), widths=[5,7,25,21,20])
df2 = pd.read_fwf(io.StringIO(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


답변

이 문제에 직면했지만이 게시물을 찾기 전에 답변을 찾았습니다.

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.StringIO(texts[0]), widths=[5,7,25,17,20], parse_dates=[4])
df2 = pd.read_fwf(io.StringIO(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

그건 그렇고, 당신이 IPython Notebook에 있다면, 셀이 다르거 나, 같거나 왼쪽 / 오른쪽 null인지에 따라 색상을 부여하기 위해 색상 차이 기능을 사용할 수 있습니다 :

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))


답변

두 개의 데이터 프레임에 동일한 ID가 있으면 변경된 내용을 찾는 것이 실제로 쉽습니다. 그냥하는 frame1 != frame2것은 당신에게 각각 True변경된 데이터 인 부울 DataFrame을 줄 것 입니다. 그로부터을 수행하여 변경된 각 행의 색인을 쉽게 얻을 수 있습니다 changedids = frame1.index[np.any(frame1 != frame2,axis=1)].


답변

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