두 데이터 프레임 사이에서 변경된 내용을 정확하게 강조하려고합니다.
두 개의 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 테이블을 출력하는 것입니다.
- 변경된 행을 식별합니다 (int, float, boolean, string 일 수 있음)
-
소비자는 동일한 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