데이터 수집 혹은 연동을 할 때 중복된 데이터 없이 유일한 값만 갖고싶다면


아래를 봐주세요


우선 아래와 같이 샘플데이터를 만들어 줍니다




import pandas as pd

df = pd.DataFrame(columns=['id', 'desc'])

df.loc[0] = ["sbs", "aaa"]
df.loc[1] = ["kbs", "bbb"]
df.loc[2] = ["mbc", "ccc"]
df.loc[3] = ["ebs", "ddd"]
df.loc[4] = ["kbs", "eee"]




for index, row in df.iterrows():
print("id=%s, desc=%s" %(row.id, row.desc))



출력을 해볼까요?


id=sbs, desc=aaa

id=kbs, desc=bbb

id=mbc, desc=ccc

id=ebs, desc=ddd

id=kbs, desc=eee


id가 동일한게 'kbs'가 있군요 desc는 다르지만요


id로 중복제거를 해볼까요?


df2 = df.drop_duplicates('id', keep='first')

for index, row in df2.iterrows():
print("id=%s, desc=%s" % (row.id, row.desc))


drop_duplicates(중복제거할 기준컬럼, keep='first' or 'last')


keep은 first면 처음걸 두고 나머지를 제거할테고


last라면 반대로 마지막을 두고 그이전 중복데이터를 제거하겠죠


위의 출력결과를 보면


id=sbs, desc=aaa
id=kbs, desc=bbb
id=mbc, desc=ccc
id=ebs, desc=ddd

마지막 데이터인 kbs의 desc가 eee인 row가 제거되었네요 


잘됩니다


이번에는


df3 = df.drop_duplicates('id', keep='last')

for index, row in df3.iterrows():
print("id=%s, desc=%s" % (row.id, row.desc))

마지막을 남기도록 옵션을 주고 중복제거를 해봅니다


결과는 역시


id=sbs, desc=aaa
id=mbc, desc=ccc
id=ebs, desc=ddd
id=kbs, desc=eee

네 잘되네요


이번에는 컬럼을 하나 추가해서 샘플 데이터를 만들어 볼게요


df4 = pd.DataFrame(columns=['id', 'desc', 'num'])

df4.loc[0] = ["sbs", "aaa", "1"]
df4.loc[1] = ["kbs", "bbb", "1"]
df4.loc[2] = ["mbc", "ccc", "1"]
df4.loc[3] = ["ebs", "ddd", "3"]
df4.loc[4] = ["kbs", "eee", "2"]
df4.loc[5] = ["kbs", "bbb", "2"]

이렇게 num라는 컬럼을 하나 추가해주고


df5 = df4.drop_duplicates(['id', 'desc', 'num'], keep='last')

for index, row in df5.iterrows():
print("id=%s, desc=%s" % (row.id, row.desc))

중복제거를 해볼까요?


id=sbs, desc=aaa
id=kbs, desc=bbb
id=mbc, desc=ccc
id=ebs, desc=ddd
id=kbs, desc=eee
id=kbs, desc=bbb

중복이 없으니 중복제거가 안되네요


중복제거 기준에서 num을 빼준다면 "kbs"와 "bbb"가 중복되어 row를 제거할수 있겠네요


df5 = df4.drop_duplicates(['id', 'desc'], keep='last')
for index, row in df5.iterrows():
print("id=%s, desc=%s, num=%s" % (row.id, row.desc, row.num))

위와 같이 id와 desc를 중복 기준으로 주고 keep 라스트를 남긴다 자 결과는


id=sbs, desc=aaa, num=1
id=mbc, desc=ccc, num=1
id=ebs, desc=ddd, num=3
id=kbs, desc=eee, num=2
id=kbs, desc=bbb, num=2


중복제거 잘 되고 데이터도 마지막게 잘 남았네요


아래행 데이터가 아닌 전의 데이터를 유지하고 싶으면 어떻게 하면될까요?


keep 옵션만 first로 주면 되겠죠?


중복제거는 참 유용한거 같습니다


id기준 1000건 연동해야 한다면


중복제거 후 800건이 되면 200건은 연동하지 않아도 될테니까요


이상으로 pandas로 중복제거하는 방법이었습니다.

1편에서 pandas를 이용해 xml정보를 csv엑셀로 저장하는것을 해보았다.


그러다 특정 정보에서 charge라는 요금항목을 누락해서 xml return해주는 경우가 발생해서


오류가 났다.


☆ 정상 xml 리턴


<item>
<arrPlaceNm>전주</arrPlaceNm>
<arrPlandTime>201707011100</arrPlandTime>
<charge>20500</charge>
<depPlaceNm>상봉</depPlaceNm>
<depPlandTime>201707010800</depPlandTime>
<gradeNm>우등</gradeNm>
<routeId>NAEK040602</routeId>
</item>

누락된 xml 리턴

<item>
<arrPlaceNm>안동</arrPlaceNm>
<arrPlandTime>201707011000</arrPlandTime>
<depPlaceNm>서울경부</depPlaceNm>
<depPlandTime>201707010720</depPlandTime>
<gradeNm>우등</gradeNm>
<routeId>NAEK010840</routeId>
</item>



df.loc[i] = [item.arrplacenm.text, item.arrplandtime.text, item.charge.text, item.depplacenm.text,
AttributeError: 'NoneType' object has no attribute 'text'

Process finished with exit code 1


조건을 주어 에러를 방지해야하겠다.


기존의 코드는 이러했다.


for item in items.findAll("item"):

df.loc[i] = [item.arrplacenm.text, item.arrplandtime.text, item.charge.text, item.depplacenm.text,
item.depplandtime.text, item.gradenm.text, item.routeid.text]

i=i+1


에러방지를 위해 아래와 같이 추가해 주었다.


charge = "-"

for item in items.findAll("item"):

if (item.charge != None):
charge = item.charge.text
else:
charge = "-"

df.loc[i] = [item.arrplacenm.text, item.arrplandtime.text, charge, item.depplacenm.text,
item.depplandtime.text, item.gradenm.text, item.routeid.text]

i=i+1




item에 charge가 있을때만 가져오고 없을때는 -로 저장되도록 수정하고 정상적으로 작동되었다.


다른값들도 혹시나 하는 오류를 방지를 위해서 위와같이 변경해줘도 좋을것 같다.


# -*- coding: utf-8 -*-

import os
from bs4 import BeautifulSoup
from urllib.request import urlopen
import pandas as pd

output_path = "output"
output_file = "고속_test.csv"
if not os.path.exists(output_path):
os.makedirs(output_path)

df = pd.DataFrame(columns=['arrPlaceNm', 'arrPlandTime', 'charge', 'depPlaceNm', 'depPlandTime', 'gradeNm', 'routeId']) # 엑셀 헤더정보

i=0

url = 'xml정보 주소'

data = urlopen(url).read()
soup = BeautifulSoup(data, "html.parser")
items = soup.find("items")

for item in items.findAll("item"):

df.loc[i] = [item.arrplacenm.text, item.arrplandtime.text, item.charge.text, item.depplacenm.text,
item.depplandtime.text, item.gradenm.text, item.routeid.text]

i=i+1

df.to_csv(os.path.join(output_path, output_file), encoding='euc-kr', index=False)

위와 같이 pandas를 이용해 어렵지 않게 csv로 원하는 정보를 얻을수 있었다.


하지만 여기에서 문제가 발생했다.



위와같이 YYYYMMDDHHmm의 데이터를 지수형으로 표시해주고있다.

Ctrl + C로 복사후 메모장 같은 곳에 붙여 넣어도 지수형으로 붙여 넣어진다.


위 문제가 생긴 2개의 정보에 아래와 같이 코드를 추가해 주었다. 


df['arrPlandTime'] = '="' + df['arrPlandTime'] + '"'
df['depPlandTime'] = '="' + df['depPlandTime'] + '"'

 

이제 다시 엑셀을 확인해보자



엑셀에도 이쁘게 잘 나오고


복사 후 메모장 등에 붙여넣어도 정확하게 표시된다.



+ Recent posts