지난번 포스팅에서는 웹에서 JSON 포맷 파일을 읽어와서 pandas DataFrame으로 변환하는 방법에 대해서 소개하였습니다. 


이번 포스팅에서는 JSON과 함께 웹 애플리케이션에서 많이 사용하는 데이터 포맷인 XML (Extensible Markup Language) 을 Python을 사용하여 웹으로 부터 읽어와서 파싱(parsing XML), pandas DataFrame으로 변환하여 분석과 시각화하는 방법을 소개하겠습니다. 


XML (Extensible Markup Language) 인간과 기계가 모두 읽을 수 있는 형태로 문서를 인코딩하는 규칙의 집합을 정의하는 마크업 언어(Markup Language) 입니다. XML의 설계 목적은 단순성, 범용성, 인터넷에서의 활용성을 강조점을 둡니다. XML은 다양한 인간 언어들을 유니코드를 통해 강력하게 지원하는 텍스트 데이터 포맷입니다. 비록 XML의 설계가 문서에 중점을 두고는 있지만, XML은 임의의 데이터 구조를 띠는 웹 서비스와 같은 용도의 재표현을 위한 용도로 광범위하게 사용되고 있습니다. 

- from wikipedia (https://en.wikipedia.org/wiki/XML) -


XML 은 아래와 같이 생겼는데요, HTML, JSON과 왠지 비슷하게 생겼지요? 



[ XML format data 예시 ]



<CATALOG>
<CD>
<TITLE>Empire Burlesque</TITLE>
<ARTIST>Bob Dylan</ARTIST>
<COUNTRY>USA</COUNTRY>
<COMPANY>Columbia</COMPANY>
<PRICE>10.90</PRICE>
<YEAR>1985</YEAR>
</CD>
<CD>
<TITLE>Hide your heart</TITLE>
<ARTIST>Bonnie Tyler</ARTIST>
<COUNTRY>UK</COUNTRY>
<COMPANY>CBS Records</COMPANY>
<PRICE>9.90</PRICE>
<YEAR>1988</YEAR>
</CD>

<CD>

<TITLE>Unchain my heart</TITLE>
<ARTIST>Joe Cocker</ARTIST>
<COUNTRY>USA</COUNTRY>
<COMPANY>EMI</COMPANY>
<PRICE>8.20</PRICE>
<YEAR>1987</YEAR>
</CD>

</CATALOG> 


- source: https://www.w3schools.com/xml/cd_catalog.xml -





[ Python으로 웹에서 XML 데이터를 읽어와서 pandas DataFrame으로 만들기 코드 예제 ]




(1) Import Libraries


먼저 XML 을 파싱하는데 필요한 xml.etree.ElementTree 모듈과 웹 사이트에 접속해서 XML 파일을 읽을 수 있도록 요청하는 urllib 모듈을 불러오겠습니다. XML 데이터를 나무(Tree)에 비유해서 뿌리(root)부터 시작하여 줄기, 가지, 잎파리까지 단계적으로 파싱한다는 의미에서 모듈 이름이 xml.etree.ElementTree 라고 생각하면 됩니다. 



import pandas as pd

import xml.etree.ElementTree as ET


import sys

if sys.version_info[0] == 3:

    from urllib.request import urlopen

else:

    from urllib import urlopen




Python 3.x 버전에서는 'from urllib.request import urlopen'으로 urllib 모듈의 request 메소드를 import해야 하며, 만약 Python 3.x 버전에서 아래처럼 'from urllib import urlopen' 을 사용하면 'ImportError: cannot import name 'urlopen'' 이라는 ImportError가 발생합니다. 



# If you are using Python 3.x version, then ImportError will be raised as below

from urllib import urlopen

---------------------------------------------------------------------------
ImportError                               Traceback (most recent call last)
<ipython-input-2-dbf1dbb53f94> in <module>()
----> 1 from urllib import urlopen

ImportError: cannot import name 'urlopen'





(2) Open URL and Read XML data from Website URL


이제 "https://www.w3schools.com/xml/cd_catalog.xml" 사이트에서 XML 포맷의 CD catalog 정보를 문자열(string)로 읽어와보겠습니다. 



url = "https://www.w3schools.com/xml/cd_catalog.xml"

response = urlopen(url).read()

xtree = ET.fromstring(response)


xtree

<Element 'CATALOG' at 0x00000219E77DCCC8>

 




(3) Parsing XML data into text by iterating through each node of the tree


다음으로 for loop을 돌면서 나무의 노드들(nodes of tree)에서 필요한 정보를 찾아 파싱(find and parse XML data)하여 텍스트 데이터(text)로 변환하여 사전형(Dictionary)의 키, 값의 쌍으로 차곡차곡 저장(append)을 해보겠습니다



rows = []


# iterate through each node of the tree

for node in xtree:

    n_title = node.find("TITLE").text

    n_artist = node.find("ARTIST").text

    n_country = node.find("COUNTRY").text

    n_company = node.find("COMPANY").text

    n_price = node.find("PRICE").text

    n_year = node.find("YEAR").text

    

    rows.append({"title": n_title, 

                 "artist": n_artist, 

                 "country": n_country, 

                 "company": n_company, 

                 "price": n_price, 

                 "year": n_year})

 




(4) Convert XML text data into pandas DataFrame



# convert XML data to pandas DataFrame

columns = ["title", "artist", "country", "company", "price", "year"]

catalog_cd_df = pd.DataFrame(rows, columns = columns)


catalog_cd_df.head(10)

titleartistcountrycompanypriceyear
0Empire BurlesqueBob DylanUSAColumbia10.901985
1Hide your heartBonnie TylerUKCBS Records9.901988
2Greatest HitsDolly PartonUSARCA9.901982
3Still got the bluesGary MooreUKVirgin records10.201990
4ErosEros RamazzottiEUBMG9.901997
5One night onlyBee GeesUKPolydor10.901998
6Sylvias MotherDr.HookUKCBS8.101973
7Maggie MayRod StewartUKPickwick8.501990
8RomanzaAndrea BocelliEUPolydor10.801996
9When a man loves a womanPercy SledgeUSAAtlantic8.701987





(5) Change data type from string object to float64, int32 for numeric data


아래에 df.dtypes 로 각 칼럼의 데이터 형태를 확인해보니 전부 문자열 객체(string object)입니다. astype()을 이용하여 칼럼 중에서 price는 float64, year는 int32로 변환을 해보겠습니다. 



catalog_cd_df.dtypes

title      object
artist     object
country    object
company    object
price      object
year       object
dtype: object


import numpy as np

catalog_cd_df = catalog_cd_df.astype({'price': np.float

                                      'year': int})


catalog_cd_df.dtypes

title       object
artist      object
country     object
company     object
price      float64
year         int32
dtype: object





(6) Calculate mean value of price by Country and plot bar plot it



country_mean = catalog_cd_df.groupby('country').price.mean()

country_mean

country
EU        9.320000
Norway    7.900000
UK        8.984615
USA       9.385714
Name: price, dtype: float64



country_mean_df = pd.DataFrame(country_mean).reset_index()


import seaborn as sns

sns.barplot(x='country', y='price', data=country_mean_df)

plt.show()


 



이상으로 웹에서 XML 데이터를 Python으로 읽어와서 파싱 후 pandas DataFrame으로 변환하는 방법에 대한 소개를 마치겠습니다. 



Python으로 JSON 파일 읽기, 쓰기는 https://rfriend.tistory.com/474 를 참고하세요. 

Python으로 YAML 파일 읽기, 쓰기는 https://rfriend.tistory.com/540 를 참고하세요. 


많은 도움이 되었기를 바랍니다. 


이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾹 눌러주세요. :-)


728x90
반응형
Posted by Rfriend
,

지난번 포스팅에서는 Python의 json.dumps() 를 사용해서 JSON 포맷 데이터를 쓰거나, json.loads()를 사용해서 JSON 포맷 데이터를 python으로 읽어오는 방법(https://rfriend.tistory.com/474)을 소개하였습니다. 


이번 포스팅에서는 이어서 웹에 있는 JSON 포맷 데이터를 Python으로 읽어와서 pandas DataFrame으로 만드는 방법(How to read JSON formate data from WEB API and convert it to pandas DataFrame in python)을 소개하겠습니다. 





JSON 포맷 파일을 가져올 수 있는 사이트로 "Awesome JSON Datasets (https://github.com/jdorfman/awesome-json-datasets)" 를 예로 들어서 설명해보겠습니다. 


여러개의 JSON Datasets 이 올라가 있는데요, 이중에서 'Novel Prize' JSON 포맷 데이터(http://api.nobelprize.org/v1/prize.json)를 읽어와서 DataFrame으로 만들어보겠습니다. 




 (1) API 웹 사이트에서 JSON 포맷 자료를 Python으로 읽어오기



이제 urllib 모듈의 rulopen 함수를 사용해서 JSON 데이터가 있는 URL로 요청(request)을 보내서 URL을 열고 JSON 데이터를 읽어와서, python의 json.loads() 를 사용하여 novel_prize_json 이라 이름의 Python 객체로 만들어보겠습니다.  



# parse a JSON string using json.loads() method : returns a dictionary

import json

import urllib

import pandas as pd


# API request to the URL

import sys


if sys.version_info[0] == 3:

    from urllib.request import urlopen # for Python 3.x

else:

    from urllib import urlopen           # for Python 2.x


with urlopen("http://api.nobelprize.org/v1/prize.json") as url:

    novel_prize_json_file = url.read()




urllib 모듈의 (web open) request 메소드를 불러오 때 Python 2.x 버전에서는 from urllib import urlopen 을 사용하는 반면, Python 3.x 버전에서는 from urllib.request import urlopen 을 사용합니다. 따라서 만약 Python 3.x 사용자가 아래처럼 (Python 2.x 버전에서 사용하는) from urllib import urlopen 이라고 urlopen을 importing 하려고 하면 ImportError: cannot import name 'urlopen' 이라는 에러가 납니다. 


# ImportError: cannot import name 'urlopen' at python 3.x

from urllib import urlopen # It's only for Python 2.x. It's not working at Python 3.x


with urlopen("http://api.nobelprize.org/v1/prize.json") as url:

    novel_prize_json_file = url.read()


---------------------------------------------------------------------------
ImportError                               Traceback (most recent call last)
<ipython-input-1-81c8fae1a1fd> in <module>()
      1 # API request to the URL
----> 2 from urllib import urlopen
      3 
      4 with urlopen("http://api.nobelprize.org/v1/prize.json") as url:
      5     novel_prize_json_file = url.read()

ImportError: cannot import name 'urlopen'



다음으로, 위에서 읽어온 JSON 포맷 데이터를 Python의 json.loads() 메소드를 이용해서 decoding 해보겠습니다. 이때 decode('utf-8') 로 설정해주었습니다. 


# decoding to python object

novel_prize_json = json.loads(novel_prize_json_file.decode('utf-8'))



decoding을 할 때 'utf-8' 을 설정을 안해주니 아래처럼 TypeError 가 나네요. (TypeError: the JSON object must be str, not 'builtin_function_or_method')


# decoding TypeError. decode using decode('utf-8')

novel_prize_json = json.loads(novel_prize_json_file.decode)

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-9-1fda68be6386> in <module>()
      1 # decoding TypeError
----> 2 novel_prize_json = json.loads(novel_prize_json_file.decode)

C:\Users\admin\Anaconda3\lib\json\__init__.py in loads(s, encoding, cls, object_hook, parse_float, parse_int, parse_constant, object_pairs_hook, **kw)
    310     if not isinstance(s, str):
    311         raise TypeError('the JSON object must be str, not {!r}'.format(
--> 312                             s.__class__.__name__))
    313     if s.startswith(u'\ufeff'):
    314         raise JSONDecodeError("Unexpected UTF-8 BOM (decode using utf-8-sig)",

TypeError: the JSON object must be str, not 'builtin_function_or_method'




Novel Prize JSON 파일을 Python 객체로 읽어왔으니, keys() 메소드로 키를 확인해보겠습니다. 그리고 'prizes' 키의 첫번째 데이터(novel_prize_json['prizes'][0])인 물리학(Physics) 분야 노벨상 수상자 정보를 인쇄해보겠습니다. 



novel_prize_json.keys()

dict_keys(['prizes'])


novel_prize_json['prizes'][0].keys()

dict_keys(['laureates', 'year', 'overallMotivation', 'category'])


novel_prize_json['prizes'][0]

{'category': 'physics',
 'laureates': [{'firstname': 'Arthur',
   'id': '960',
   'motivation': '"for the optical tweezers and their application to biological systems"',
   'share': '2',
   'surname': 'Ashkin'},
  {'firstname': 'Gérard',
   'id': '961',
   'motivation': '"for their method of generating high-intensity, ultra-short optical pulses"',
   'share': '4',
   'surname': 'Mourou'},
  {'firstname': 'Donna',
   'id': '962',
   'motivation': '"for their method of generating high-intensity, ultra-short optical pulses"',
   'share': '4',
   'surname': 'Strickland'}],
 'overallMotivation': '"for groundbreaking inventions in the field of laser physics"', 

'year': '2018'} 




가독성을 높이기 위해서 json.dumps(obj, indent=4) 를 사용해서 4칸 들여쓰기 (indentation)을 해보겠습니다. 



print(json.dumps(novel_prize_json['prizes'][0], indent=4))

{
    "laureates": [
        {
            "share": "2",
            "id": "960",
            "surname": "Ashkin",
            "motivation": "\"for the optical tweezers and their application to biological systems\"",
            "firstname": "Arthur"
        },
        {
            "share": "4",
            "id": "961",
            "surname": "Mourou",
            "motivation": "\"for their method of generating high-intensity, ultra-short optical pulses\"",
            "firstname": "G\u00e9rard"
        },
        {
            "share": "4",
            "id": "962",
            "surname": "Strickland",
            "motivation": "\"for their method of generating high-intensity, ultra-short optical pulses\"",
            "firstname": "Donna"
        }
    ],
    "year": "2018",
    "overallMotivation": "\"for groundbreaking inventions in the field of laser physics\"",
    "category": "physics"
}

 



키(keys)를 기준으로 정렬하는 것까지 포함해서 다시 한번 프린트를 해보겠습니다. 



print(json.dumps(novel_prize_json['prizes'][0], indent=4, sort_keys=True))

{
    "category": "physics",
    "laureates": [
        {
            "firstname": "Arthur",
            "id": "960",
            "motivation": "\"for the optical tweezers and their application to biological systems\"",
            "share": "2",
            "surname": "Ashkin"
        },
        {
            "firstname": "G\u00e9rard",
            "id": "961",
            "motivation": "\"for their method of generating high-intensity, ultra-short optical pulses\"",
            "share": "4",
            "surname": "Mourou"
        },
        {
            "firstname": "Donna",
            "id": "962",
            "motivation": "\"for their method of generating high-intensity, ultra-short optical pulses\"",
            "share": "4",
            "surname": "Strickland"
        }
    ],
    "overallMotivation": "\"for groundbreaking inventions in the field of laser physics\"",
    "year": "2018"
}

 




 (2) JSON 포맷 데이터를 pandas DataFrame으로 만들기


다음으로 Python으로 불러온 JSON 포맷의 데이터 중의 일부분을 indexing하여 pandas DataFrame으로 만들어보겠습니다. 


예로, ['prizes'][0] 은 물리학(physics) 노벨상이며, ['prizes'][0]['laureates'] 로 물리학 노벨상 수상자 정보만 선별해서 pd.DataFrame() 으로 DataFrame을 만들어보겠습니다. 


novel_prize_physics = pd.DataFrame(novel_prize_json['prizes'][0]["laureates"])


novel_prize_physics

firstnameidmotivationsharesurname
0Arthur960"for the optical tweezers and their applicatio...2Ashkin
1Gérard961"for their method of generating high-intensity...4Mourou
2Donna962"for their method of generating high-intensity...4Strickland



DataFrame을 만들 때 칼럼 순서를 columns 로 지정을 해줄 수 있습니다. 


novel_prize_physics = pd.DataFrame(novel_prize_json['prizes'][0]["laureates"]

                                   columns = ['id', 'firstname', 'surname', 'share', 'motivation'])


novel_prize_physics

idfirstnamesurnamesharemotivation
0960ArthurAshkin2"for the optical tweezers and their applicatio...
1961GérardMourou4"for their method of generating high-intensity...
2962DonnaStrickland4"for their method of generating high-intensity...


많은 도움이 되었기를 바랍니다 .


다음 포스팅에서는 웹에서 XML 포맷 데이터를 Python으로 읽어와서 pandas DataFrame으로 만드는 방법을 소개하겠습니다. 


이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾹 눌러주세요. 



728x90
반응형
Posted by Rfriend
,

json.org의 JSON 소개 내용에 따르면, JSON (JavaScript Object Notation) 은 XML, YAML 과 함께 효율적으로 데이터를 저장하고 교환(exchange data)하는데 사용하는 텍스트 데이터 포맷 중의 하나입니다. JSON은 사람이 읽고 쓰기에 쉬우며, 또한 기계가 파싱하고 생성하기도에 쉽습니다. JSON은 그 이름에서 유추할 수 있듯이 JavaScript의 프로그래밍 언어의 부분에 기반하고 있으며, C-family 프로그램밍 언어 (C, C++, C#, Java, JavaScript, Perl, Python 등)의 규약을 따르고 있어서 C-family 프로그래밍 언어 간 데이터를 교환하는데 적합합니다. 

JSON은 아래의 두개의 구조로 이루어져 있습니다. 

  • 이름/값 쌍의 집합 (A collection of name/value pairs): object, record, struct, dictionary, hash table, keyed list, associative array
  • 정렬된 값의 리스트 (An ordered list of values): array, vector, list, sequence

홍길동이라는 이름의 학생에 대한 정보를 포함하고 있는 JSON 데이터 포맷의 예를 들어보겠습니다.

{

    "1.FirstName": "Gildong",
    "2.LastName": "Hong",
    "3.Age": 20,
    "4.University": "Yonsei University",
    "5.Courses": [
        {
            "Classes": [
                "Probability",
                "Generalized Linear Model",
                "Categorical Data Analysis"
            ],
            "Major": "Statistics"
        },
        {
            "Classes": [
                "Data Structure",
                "Programming",
                "Algorithms"
            ],
            "Minor": "ComputerScience"
        }
    ]
}


그러면, 이번 포스팅에서는 

(1) Python 객체를 JSON 데이터로 쓰기, 직렬화, 인코딩 (Write Python object to JSON, Serialization, Encoding)

(2) JSON 포맷 데이터를 Python 객체로 읽기, 역직렬화, 디코딩 (Read JSON to Python, Deserialization, Decoding)

하는 방법을 소개하겠습니다. 


위의 Python - JSON 간 변환 표(conversion table b/w python and JSON)에서 보는 바와 같이, python의 list, tuple 이 JSON의 array로 변환되며, JSON의 array는 pythonhon의 list로 변환됩니다. 따라서 Python의 tuple을 JSON으로 변환하면 JSON array가 되며, 이를 다시 Python으로 재변환하면 이땐 python의 tuple이 아니라 list로 변환된다는 점은 인식하고 사용하기 바랍니다. 


 (1) Python 객체를 JSON 데이터로 쓰기, 직렬화, 인코딩: json.dumps()
      (Write Python object to JSON, Serialization, Encoding)

python 객체를 JSON 데이터로 만들어서 쓰기 위해서는 파이썬의 내장 json 모듈이 필요합니다. 

 import json


아래와 같은 홍길동 이라는 학생의 정보를 담고 있는 사전형 자료(dictionary)를 json.dump()와 json.dumps() 의 두가지 방법으로 JSON 포맷 데이터로 만들어보겠습니다. 

student_data = {
    "1.FirstName": "Gildong",
    "2.LastName": "Hong",
    "3.Age": 20, 
    "4.University": "Yonsei University",
    "5.Courses": [
        {
            "Major": "Statistics", 
            "Classes": ["Probability", 
                        "Generalized Linear Model", 
                        "Categorical Data Analysis"]
        }, 
        {
            "Minor": "ComputerScience", 
            "Classes": ["Data Structure", 
                        "Programming", 
                        "Algorithms"]
        }
    ]
} 


(2-1) with open(): json.dump() 를 사용해서 JSON 포맷 데이터를 디스크에 쓰기

with open("student_file.json", "w") 로 "student_file.json" 이름의 파일을 쓰기("w") 모드로 열어놓고, json.dump(student_data, json_file) 로 직렬화해서 JSON으로 내보내고자 하는 객체 student_data를, 직렬화된 데이터가 쓰여질 파일 json_file 에 쓰기를 해주었습니다. 

import json

with open("student_file.json", "w") as json_file:

    json.dump(student_data, json_file)


그러면 아래의 화면캡쳐에서 보는 바와 같이 'student_file.json'이라는 이름의 JSON 포맷 데이터가 새로 생성되었음을 알 수 있습니다.



(2-2) json.dumps()를 사용해서 JSON 포맷 데이터를 메모리에 만들기

만약 메모리 상에 JSON 포맷 데이터를 만들어놓고 python에서 계속 작업을 하려면 json.dumps() 를 사용합니다.

import json

st_json = json.dumps(student_data)

print(st_json)

{"5.Courses": [{"Classes": ["Probability", "Generalized Linear Model", "Categorical Data Analysis"], "Major": "Statistics"}, {"Minor": "ComputerScience", "Classes": ["Data Structure", "Programming", "Algorithms"]}], "3.Age": 20, "2.LastName": "Hong", "4.University": "Yonsei University", "1.FirstName": "Gildong"}


이때 만약 json.dumps()가 아니라 json.dump() 처럼 's'를 빼먹으면 TypeError가 발생하므로 주의하세요. 

# use json.dumps() instead of json.dump()

st_json = json.dump(student_data)

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-5-ac881d00bbcb> in <module>()
----> 1 st_json = json.dump(student_data)

TypeError: dump() missing 1 required positional argument: 'fp'


json.dumps()로 파이썬 객체를 직렬화해서 JSON으로 쓸 때 사람이 보기에 좀더 쉽도록 'indent = int' 로 들여쓰기(indentation) 옵션을 설정할 수 있습니다. 아래 예시는 indent=4 로 설정한건데요, 한결 보기에 가독성이 좋아졌습니다. 


import json

st_json2 = json.dumps(student_data, indent=4)

print(st_json2)

{

    "3.Age": 20,
    "5.Courses": [
        {
            "Classes": [
                "Probability",
                "Generalized Linear Model",
                "Categorical Data Analysis"
            ],
            "Major": "Statistics"
        },
        {
            "Minor": "ComputerScience",
            "Classes": [
                "Data Structure",
                "Programming",
                "Algorithms"
            ]
        }
    ],
    "1.FirstName": "Gildong",
    "4.University": "Yonsei University",
    "2.LastName": "Hong"
}


'sort_keys=True' 를 설정해주면 키(keys)를 기준으로 정렬해서 직렬화하여 내보낼 수도 있습니다. 

import json

st_json3 = json.dumps(student_data, indent=4, sort_keys=True)

print(st_json3)

{
    "1.FirstName": "Gildong",
    "2.LastName": "Hong",
    "3.Age": 20,
    "4.University": "Yonsei University",
    "5.Courses": [
        {
            "Classes": [
                "Probability",
                "Generalized Linear Model",
                "Categorical Data Analysis"
            ],
            "Major": "Statistics"
        },
        {
            "Classes": [
                "Data Structure",
                "Programming",
                "Algorithms"
            ],
            "Minor": "ComputerScience"
        }
    ]
}




 (2) JSON 포맷 데이터를 Python 객체로 읽기, 역직렬화, 디코딩: json.loads()
      (Read JSON to Python, Deserialization, Decoding)


(2-1) 디스크에 있는 JSON 포맷 데이터를 json.load()를 사용하여 Python 객체로 읽어오기 (역직렬화, 디코딩 하기)

이어서, (1)번에서 with open(): json.dump() 로 만들어놓은 JSON 포맷의 데이터 "student_file.json" 를 Python 으로 역질렬화(deserialization)해서 읽어와 보겠습니다. with open("student_file.json", "r") 로 읽기 모드("r")로 JSON파일을 열어 후에, json.load(st_json)으로 디코딩하였습니다. 

import json

with open("student_file.json", "r") as st_json:

    st_python = json.load(st_json)


st_python

{'1.FirstName': 'Gildong',
 '2.LastName': 'Hong',
 '3.Age': 20,
 '4.University': 'Yonsei University',
 '5.Courses': [{'Classes': ['Probability',
    'Generalized Linear Model',
    'Categorical Data Analysis'],
   'Major': 'Statistics'},
  {'Classes': ['Data Structure', 'Programming', 'Algorithms'],
   'Minor': 'ComputerScience'}]}


이때 json.loads() 처럼 's'를 붙이면 TypeError: the JSON object must be str, not 'TextIOWrapper'가 발생합니다. (json.loads()가 아니라 json.load() 를 사용해야 함)

# use json.load() instead of json.loads()

with open("student_json_file.json", "r") as st_json:

    st_python = json.loads(st_json)

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-53-c39634419df6> in <module>()
      1 with open("student_json_file.json", "r") as st_json:
----> 2     st_python = json.loads(st_json)

C:\Users\admin\Anaconda3\lib\json\__init__.py in loads(s, encoding, cls, object_hook, parse_float, parse_int, parse_constant, object_pairs_hook, **kw)
    310     if not isinstance(s, str):
    311         raise TypeError('the JSON object must be str, not {!r}'.format(
--> 312                             s.__class__.__name__))
    313     if s.startswith(u'\ufeff'):
    314         raise JSONDecodeError("Unexpected UTF-8 BOM (decode using utf-8-sig)",

TypeError: the JSON object must be str, not 'TextIOWrapper'



(2-2) 메모리에 있는 JSON 포맷 데이터를 json.loads()로 Python 객체로 읽기 (역직렬화, 디코딩하기)

import json

st_python2 = json.loads(st_json3)

st_python2

{'1.FirstName': 'Gildong',
 '2.LastName': 'Hong',
 '3.Age': 20,
 '4.University': 'Yonsei University',
 '5.Courses': [{'Classes': ['Probability',
    'Generalized Linear Model',
    'Categorical Data Analysis'],
   'Major': 'Statistics'},
  {'Classes': ['Data Structure', 'Programming', 'Algorithms'],
   'Minor': 'ComputerScience'}]}


이때 만약 json.loads() 대신에 's'를 빼고 json.load()를 사용하면 AttributeError: 'str' object has no attribute 'read' 가 발생하니 주의하기 바랍니다. 

# use json.loads() instead of json.load()

st_python2 = json.load(st_json3)

---------------------------------------------------------------------------

AttributeError                            Traceback (most recent call last)
<ipython-input-54-9de49903fef6> in <module>()
----> 1 st_python2 = json.load(st_json3)

C:\Users\admin\Anaconda3\lib\json\__init__.py in load(fp, cls, object_hook, parse_float, parse_int, parse_constant, object_pairs_hook, **kw)
    263 
    264     """
--> 265     return loads(fp.read(),
    266         cls=cls, object_hook=object_hook,
    267         parse_float=parse_float, parse_int=parse_int,

AttributeError: 'str' object has no attribute 'read'


많은 도움이 되었기를 바랍니다. 

다음번 포스팅에서는 '웹(API)으로 부터 JSON 포맷 자료를 Python으로 읽어와서 pandas DataFrame으로 만드는 방법(https://rfriend.tistory.com/475)을 소개하겠습니다. 

Python으로 XML 파일 읽기, 쓰기는 https://rfriend.tistory.com/477 를 참고하세요. 

Python으로 YAML 파일 읽기, 쓰기는 https://rfriend.tistory.com/540 를 참고하세요. 

이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾹 눌러주세요.  :-)


728x90
반응형
Posted by Rfriend
,

이전 포스팅에서 사전 자료형(dictionary data type)을 만드는 방법과 기본 사용법(https://rfriend.tistory.com/333), 사전 자료형 내장함수와 메소드(https://rfriend.tistory.com/334)에 대해서 설명한 적이 있습니다. 


사전 자료형을 중괄호로 묶여서 {키(key) : 값(value)} 의 쌍으로 이루어진다고 했으며, hash table type 으로 키를 해싱해놓고 있다가, 키를 기준으로 값을 찾으려고 하면 매우 빠른 속도로 찾아주는 효율적이고 편리한 자료형이라고 소개를 했었습니다. 


이번 포스팅에서는 사전 자료형을 


(1) 키를 기준으로 오름차순 정렬 (sort by key in ascending order)

(2) 키를 기준으로 내림차순 정렬 (sort by key in descending order)

(3) 값을 기준으로 오름차순 정렬 (sort by value in ascending order)

(4) 값을 기준으로 내림차순 정렬 (sort by value in descending order) 


하는 방법을 알아보겠습니다. 




{키 : 값} 쌍으로 이루어진 간단한 예제 사전 자료형을 만들어보겠습니다. (프로그래밍 언어별 인기도)


# make a dictionary

pgm_lang = {

    "java": 20, 

    "javascript": 8, 

    "c": 7,  

    "r": 4, 

    "python": 28 } 




 (1) 를 기준으로 오름차순 정렬 (sort by key in ascending order): sorted()


pgm_lang 사전형 자료를 키(key)를 기준으로 알파벳 오름차순으로 정렬해보겠습니다. dict.keys()를 정렬하면 keys 만 정렬된 값을 반환하며, dict.items()를 정렬하면 키(key)를 기준으로 정렬하되 키와 값을 튜플로 묶어서 정렬된 값을 반환합니다. 



sorted(pgm_lang.keys())


['c', 'java', 'javascript', 'python', 'r']



sorted(pgm_lang.items())


[('c', 7), ('java', 20), ('javascript', 8), ('python', 28), ('r', 4)]




그러면, 키를 기준으로 정렬한 (키, 값) 쌍의 튜플을 for loop 을 써서 한줄씩 인쇄를 해보겠습니다. 

첫번째 예는 일반적인 for loop이며, 두번째는 list comprehension 을 이용하여 같은 결과로 인쇄한 예입니다. 



for key, value in sorted(pgm_lang.items()):

    print(key, ":", value)


c : 7
java : 20
javascript : 8
python : 28
r : 4


# list comprehension

[print(key, ":", value) for (key, value) in sorted(pgm_lang.items())]


c : 7
java : 20
javascript : 8
python : 28
r : 4

[None, None, None, None, None]




키를 기준으로 정렬할 때, lambda 함수를 사용하여 새롭게 정의한 키(custom key function, logic)를 사용할 수도 있습니다. 아래 예는 키의 길이(length)를 기준으로 오름차순 정렬을 해본 것입니다. 



# sort a dictionary by custom key function (eg. by the length of key strings)

pgm_lang_len = sorted(pgm_lang.items(), key = lambda item: len(item[0])) # key: [0]


for key, value in pgm_lang_len:

    print(key, ":", value)


c : 7
r : 4
java : 20
python : 28
javascript : 8





 (2) 를 기준으로 내림차순 정렬 (sort by key in descending order): reverse=True


내림차순으로 정렬하려면 reverse=True 옵션을 추가해주면 됩니다. 



# sorting in reverse order

sorted(pgm_lang.keys(), reverse=True)


['r', 'python', 'javascript', 'java', 'c']



for (key, value) in sorted(pgm_lang.items(), reverse=True):

    print(key, ":", value)


r : 4
python : 28
javascript : 8
java : 20
c : 7





 (3) 을 기준으로 오름차순 정렬 (sort by value in ascending order)


값(value)을 기준으로 정렬하려면 앞서 소개했던 lambda 함수를 이용하여 키(key)로 사용할 기준이 값(value), 즉 item[1] 이라고 지정을 해주면 됩니다. (키는 item[0], 값은 item[1] 로 indexing)



sorted(pgm_lang.items(), key = lambda item: item[1]) # value: [1]


[('r', 4), ('c', 7), ('javascript', 8), ('java', 20), ('python', 28)]

 



값을 기준으로 오름차순 정렬한 결과를 for loop을 같이 사용하여 (키 : 값) 한쌍씩 차례대로 프린트를 해보겠습니다. (list comprehension 을 사용해도 결과 동일)



pgm_lang_val = sorted(pgm_lang.items(), key = lambda item: item[1])


for key, value in pgm_lang_val:

    print(key, ":", value)


r : 4
c : 7
javascript : 8
java : 20
python : 28


# list comprehension

[print(key, ":", value) for (key, value) in sorted(pgm_lang.items(), key = lambda item: item[1])]


r : 4
c : 7
javascript : 8
java : 20
python : 28
[None, None, None, None, None]





 (4) 을 기준으로 내림차순 정렬 (sort by value in descending order) 


위의 (3)번과 같이 값을 기준으로 정렬하므로 key=lambda x:x[1] 로 값(value, 즉 x[1])이 정렬 기준이라고 지정을 해주구요, 대신 내림차순이므로 reverse=True 옵션을 추가해주면 됩니다. 



# in reverse order

pgm_lang_val_reverse = sorted(pgm_lang.items()

                              reverse=True

                              key=lambda item: item[1])


for key, value in pgm_lang_val_reverse:

    print(key, ":", value)


python : 28
java : 20
javascript : 8
c : 7
r : 4




바로 위의 예에서는 for loop 순환할 때 key, value 2개 객체로 따로 따로 받았는데요, 바로 아래의 예처럼 items 로 (키, 값) 튜플로 받아서 items[0] 으로 키, items[1] 로 값을 indexing 할 수도 있습니다. 



# the same result with above

for items in pgm_lang_val_reverse:

    print(items[0], ":", items[1])


python : 28
java : 20
javascript : 8
c : 7
r : 4




이상으로 사전 자료형(dictionary)의 키, 값 기준 정렬(sorted)하는 4가지 방법 소개를 마치겠습니다. 



많은 도움이 되었기를 바랍니다. 


이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾹 눌러주세요. :-)


728x90
반응형
Posted by Rfriend
,

2019년 8월 9일 문재인 대통령님께서 10곳의 장관급 인사를 교체하는 개각을 단행하였으며, 이중 장관 인사청문회를 해야 하는 후보자가 7명입니다. 

(조국 법무부장관 후보, 은성수 금융위원장 후보, 한상혁 방송통신위원장 후보, 조성욱 공정위원장 후보, 최기영 과학기술정보통신부장관 후보, 김현수 농림축산식품부장관 후보, 이정옥 여성가족부장관 후보) 

조국 후보자에 대한 묻지마 의혹 제기 기사는 차고도 넘치는 반면에 타 후보자에 대한 뉴스 기사를 억지로 키워드 검색을 하지 않는 이상 뉴스를 접하기가 쉽지 않습니다. 

최근 한 달 기간 (2019.07.27 ~ 2019.08.26) 동안 이들 7명의 뉴스 기사 건수를 Naver 뉴스 검색을 통해서 조사를 해보았습니다. 

조국 후보자에 대한 뉴스 건수는 73,391건이고, 그 외 6명의 장관 후보자들의 평균 뉴스 건수는 1,464건입니다. 조국 후보자와 타 후부자 간에 뉴스 건수가 50배의 차이가 납니다. 

언론이 미치지 않고서야 이럴 수가 없습니다. 역대 비교 불가입니다. 

(너무나 차이가 많이 나서 검색 설정을 잘못한건가 싶을 정도인데요, 혹시 Naver의 뉴스 기사 검색에서 기사 건수 세는 알고리즘 자세히 알고 계신분은 댓글 남겨주세요.)


[ 조국 법무부장관 후보자에 대한 최근 한달(2019.7.27~2019.8.26) 뉴스 건수 비교 ]


* 검색 키워드는 '이름 부서' (조국 법무부, 은성수 금융, 한상혁 방송통신, 조성욱 공정, 최기영 과학기술, 김현수 농림축산, 이정옥 여성가족부, 황교안 법무부, 노무현 대통령) 를 사용하였습니다. 


조국 법무부 장관 후보자 청문회가 오늘부터 일주일 뒤인 9월2일~9월3일로 잡혔네요. 

궁금해서 황교안 전 법무부장관에 대한 인사청문회 시점의 뉴스 기사 건수도 한번 조사해보았습니다. 조국 후부자와의 비교를 위해서 황교안 법무부장관 후보 인사청문회가 있었던 2013년 2월 28일을 기준으로 일주일 전인 2013년 2월 21일까지 한달간의 뉴스 기사를 조사해보았습니다. (뉴스기사 조회 기간: 2013.01.21~2013.02.21 (1달), 인사청문회 일주일 전까지) 

조국 후보자의 뉴스 건수는 황교안 전 법무부장관 후보자에 대한 뉴스 건수 1,122건과는 65배 차이가 납니다. 

이게 정상이라고 생각하시나요? 이게 말이 됩니까? 황교안 전 법무부장관이 의혹 제기할 것이, 검증할 것이 별로 없었냐 하면 그건 아니잖아요? 


제가 대학생 때 '현대사회의 과제'라는 교양수업 시간에 발표 과제가 있었는데요, 저는 '조선일보와 한겨레신문 기사 모니터링'을 해서 발표를 했던 기억이 납니다. 2주 동안 매일 도서관에 가서 조선일보와 한겨레신문을 비교하면서 '같은 사건'을 두 신문사가 어떻게 '다르게' 쓰는지를 비교해볼 수 있는 기회였습니다. 

이 과제 이전에 제가 가지고 있던 신문 뉴스에 대한 생각은 "언론사는 공평하고 중립이며, 신문 기사는 사실을 전달하므로 믿어도 된다. 기자는 양심과 소명의식에 따라 신문기사를 작성한다." 였습니다. 그런데 이 2주짜리 모니터링 후 과제 발표를 할 때 이런 내용으로 발표를 했습니다. 

  • 언론은 절대 공평하지도, 중립적이지도 않다. 
  • 언론사는 각자 프레임, 아젠다를 선점하기 위해 전쟁을 벌이고 있다. 
  • 같은 사건에 대해서도 누가, 어떤 관점으로, 무슨 의도를 가지고 쓰느냐에 따라 정반대 내용이 될 수 있다.
  • 산소같은 언론의 기능은 커녕 사회에 해악을 끼치는 암적인 언론도 있다. 
  • 어떤 뉴스를 취사선택해서, 어느면에 배치하고, 헤드라인은 어떻게 뽑고, 사진과 그래프는 또 어떻게 하는냐에 따라 천당과 지옥을 오가게 할 수 있다. 

뭐, 이런 내용이었습니다. 그리고 이 발표 후에 "안티조선" 스티거 여기저기 많이 붙이고 다녔던 기억이 납니다. 그리고 조중동과 그 인터넷자회사들이 쓴 신문기사를 보면 "이 악날한 것들이 이번에는 무슨 수작을 부리려고..." 하는 생각을 가지고 신문기사를 봅니다. 


가령, 신문기사 몇 가지 예를 들어보겠습니다. 

아래는 아시아경제의 2019.08.21일자 기사의 헤드라인인데요, 마치 딸 입시, 아들 병역에 뭔가 불법적인 문제가 있는 것처럼 써놓았습니다. 부모가 미국 유학 중에 아들을 출산해서 이중국적이고, 군대를 안가겠다고 아들이 국적을 포기한것도 아니고, 내년에 군대가겠다고 하는데도 마치 스티브유처럼 국적 포기하고 병역회피한 듯한 뉘앙스로 제목을 뽑아놨습니다. 


아래 기사는 매일경제에서 2019.08.26에 기사화를 했는데요, 제목이 "서울대생 98% 법무장관 부적합"이라고 뽑아놓았습니다. 98% 반대면 이건 북한에서나 가능한 숫자인데요, 심지어 박근혜 탄핵국면에서도 탄핵찬성이 70%~80%대 사이를 왔다갔다 했다는걸 생각해보면 98%반대는 말도 안되는데요, 헤드라인을 이렇게 뽑아놓았습니다. 

매일경제 기사를 클릭(그래, 클릭하게 해서 광고비 벌려는거 다 안다...)해서 확인해보면, '스누라이프'라는 서울대생 커뮤니티에서 온라인 설문을 했는데요, 투표 참가인원이 2,151명이라고 하고, 그중에 98%가 "조국 후보자 법무부장관 부적합"이라고 응답을 했다는 겁니다. (매일경제 하면 "조국 딸 오피스텔 포르쉐 2대"라는 허위 기사 올렸다가 순삭한 경력이 있는 그 MK 맞습니다. 에휴, 황색저널리즘의 끝판왕... -_-;)

그런데 문제는 이 설문조사의 경우 성별/연령 층을 나누어서 임의로 샘플링(stratified random sampling)을 해서 조사를 한게 아니구요, 그냥 '스누라이프'라는 그룹 내 동질성이 강한 스누라이프 커뮤니티(<-- 박근혜 탄핵을 받대했다는 수구보수 성향 집단 -_-;)에 가입된 학생 중에서 설문에 응답하고 싶은 사람(self-selected)이 와서 응답을 할 경우에는 모집단(서울대생)을 절대로 대표할 수가 없다는 점입니다

조사연구에서 확률표본(random sampling)을 추출하는 이유는 무작위로 뽑는 확률표본을 통해서 편향된 표본이 추출되는 것을 방지하여 추정량의 편향(bias)을 최소화할 수 있고, 또 확률표본을 추출하여 조사할 때 표집오차(sampling error)를 계산함으로써 추정량의 정확도(presicion of estimates)를 평가할 수 있기 때문입니다. 그런데 이 '스누라이프' 커뮤니티 대상의 self-selected survey는 확률표본이 아니므로 심하게 bias 가 된, coverage error sampling error가 상당할 것으로 보이는 신뢰할 수 없는 조사인 것입니다. 

검색해보니 서울대 재학생이 28,630명인데요, 그러면 7.5%가 설문에 참여한겁니다. 저라면 "서울대학교 내에 자유한국당을 지지하는 학생 비율이 최소 7.5% 정도는 되나?"라고 추측을 해보는게 "서울대생의 98%는 조국후보자의 장관 선임을 반대해"라고 해석하는 것보다 더 합리적일 것 같습니다. -_-;

기자를 할 정도면 시장조사방법론에 대해서는 기본적으로 알고 있을 것이라고 예상을 하는데요, 알고도 이를 이딴식으로 기사화했다면 악의적인 것이구요, 모르고 기사화했다면 기자 자질이 의심스러운거지요. 


아래 기사는 서울신문에서 2019.08.25일 게재한 KBS '일요진단 라이브'의 의뢰로 진행한 설문조사 결과에 대한 기사입니다. 헤드라인을 "조국, 법무부 장관 부적합 48%... '적합'은 18% 그쳐"라고 해놓았습니다. 그리고 클릭(-_-; 결국 클릭을 하게 만드는...)해서 기사 본문을 확인해보면 "아직까지 적합.부적합 여부를 판단하기 어렵다"는 답변은 34%로 조사됐다는 내용이 나옵니다. 제가 데스크의 편집국에 있었다면 있는 그대로 "부적합 48%, 판단 보류 34%, 적합 18%"라고 제목을 뽑았을거 같습니다. 판단보류라는 34%의 민심, 일단 청문회에서 조국 후보자가 무슨 해명을 하는지 들어도 보고, 또 사법개혁, 검찰개혁에 대해서도 들어본 후에 결정하겠다는 목소리는 제목에서 쏙 빼고, "적합은 18%에 그쳐"라고 해서 마치 대부분은 국민은 조국 후보에 반대하는 듯한 인상을 주고 있습니다. 

이러한 제목뽑기가 참 악랄한 것이요, 요즘 사람들 바빠서 기사 전체를 꼼꼼하게 다 읽지 않고 제목 보고, 요약한거 보고, 중간 중간 스킵하면서 스마트폰으로 신문기사를 보잖아요. 그리고 사람들 심리에는 무의식 중에 사회적 주류 의견에 속하려는 경향성이 있습니다. 왜냐하면 다수 의견에 속하면 왕따 당하거나 비난, 비판 받을 확률이 줄어들기 때문입니다. 위의 서울신문 기사 같은 제목에 노출이 되고 하면 '아, 주류는 조국 법무부장관 후보자에 대해서 반대인가 보구나' 라는 인식이 생길 위험이 있다는 것입니다. (서울신문이 이걸 노렸을려나요?)


아래는 중앙일보에서 2019.08.25일에 [한국당도 놀랐다...10만 집회 2030 몰리자 "우리도 어리둥절"] 이라는 헤드라인으로 기사를 냈습니다.  20~30세대가 이번에 많이 화가 난 듯 합니다. 다만, 20~30세대가 자유한국당 광화문 집회에 얼마나 참석했을지 굉장히 의구심이 들구요 (몇 명이나 와야 "몰린다"는 표현을 쓸 수 있는 걸까요?), 의도적으로 현 여당에 상대적으로 우호적인 2030세대를 이참에 여당 지지로부터 멀어지게 하려는 의도가 느껴집니다. (수구보수정당 자유한국당이 2030세대를 위해서 해준게 뭐가 있다고, 무슨 염치로 2030세대에게 표를 달라고 한단 말입니까.) 세대갈등을 봉합하는게 아니라 세대갈등을 부추기는 언론!


아래 기사는 조선일보에서 2019.08.20일에 기사화한 조국 부친 묘비 기사입니다. 자유한국당 김진태 의원이 조국 법무부 장관 후보자의 부친 묘비를 찾아가서 가족의 이름이 드러난 사진을 페이스북에 게재를 했구요, 조선일보가 이를 다시 기사화했습니다. 

조국 후보자의 동생이 이혼을 한 것이 조국 후보가 법무부 장관직을 수행하는데 무슨 관련이 있다고 인권침해소지가 다분히 있는 이런 짓을 하는 지도 모르겠구요, 언론이라면 김진태 의원의 행위에 대해서 문제제기를 해도 모자랄 판에 도리어 묘비 사진까지 곁들여서 기사화까지 했습니다. 이렇게 부모, 배우자, 딸, 형제자매, 4촌에 8촌까지 엮어 아니면 말고 식으로 가정법 의혹성 기사를 무책임하게 내보내면 누가 감히 고위공직자하겠다고 나서겠습니까. 

이런 케이스들을 일일이 열거하자면 끝이 없으므로 여기서 그치겠습니다.  


의혹에 대한 기사는 넘쳐나는데 검찰개혁, 사법개혁이 왜 필요하고, 그동안 무엇이 문제였고, 역대 정권은 왜 실패를 했고, 조국 법무부 장관 후보자의 정책은 무엇이고, 기득권의 저항을 어떻게 돌파하려는지, 현실적인 정책인지 검증해보려는 언론은 찾아보기 힘듭니다. 


저는 최근의 조국 법무부장관 후보자에 대한 언론의 광기어린 아니면 말고식 언론플레이를 볼 때 고 노무현 대통령님 때가 자꾸 오버랩이 됩니다. 지금 조국 후보자와 가족들은 얼마나 힘들어 하고 있을까 안쓰럽고 걱정도 됩니다. 

고 노무현 대통령님 서거하시기 직전인 2009.04.01 ~ 2009.05.22 일 까지의 뉴스 기사 건수도 조사를 해보았습니다. 그때 논두렁 시계, 아방궁 사저 등 지금 보면 정말 치가 떨리고 용서가 안되는 거짓 뉴스 기사들을 그때 언론사들은 저주와 조롱을 섞어서 매일 기사를 내보냈던 시기입니다. 그 당시 고 노무현 대통령님에 대한 뉴스 기사 건수 17,304건도 엄청나게 많은데요, 지금 조국 후보자에 대한 뉴스 건수 73,391건은 또 얼마나 많은 건지 비교가 될 겁니다. 


조국 후보자가 후보자 의혹에 대해서는 사과는 하되 검찰개혁의 완수를 위해 후보자 사퇴는 하지 않겠다고 하니 다행입니다. 이제 청문회 날짜가 잡혔으니 조국 후보자의 의혹에 대한 해명도 들어보고, 검찰개혁에 대해 가지고 있는 정책 방향과 철학, 의지를 들어보고나서 결정을 해도 늦지 않다고 생각합니다. (자유한국당 반대로 법으로 정해진 8월30일 청문회 기한을 못지키고 다음주로 넘어간건데요, 법을 만드는 국회의원은 법을 안지켜도 되는겁니까?


저는 견제받지 않는 권력은 반드시 부패한다고 보기 때문에 현 정부가 하려고 하는 고위공직자범죄수사처(공수처) 설치라든지 검.경 수사권 조정 등의 검찰개혁에 찬성합니다. 김영상 대통령부터 해서 역대 모든 대통령이 검찰개혁을 선거 공약으로 내세웠었습니다. 하지만 그 어느 대통령도 검찰개혁을 성공하지 못했습니다. 만약 조국 법무부 후보자가 낙마를 한다면 현 정부의 검찰개혁에 대한 드라이브의 힘을 잃고 실패할 수도 있습니다. 

x 묻은 황교안도 법무부 장관을 했는데, 겨 묻은 조국 후보자라고 법무부 장관 못할 이유가 뭐겠습니까? 

조국 법무부장관 후보자님, 검찰개혁, 사법개혁 지지합니다. 

힘내시구요! 


-------------------------------------------------------------------------------


블로그 포스팅한다고 뉴스 건수 검색했던 8월 26일 저녁 10시쯤에는 73,391이었는데요, 캡쳐하려고 8월26일 자정 넘어서 8월26일까지로 다시 검색하니깐 75,292건으로 더 늘어났네요.


(덧글) '조국 법무부' 키워드로 뉴스 기사 검색을 했는데요, 개수도 많고 타 후보들과 차이가 너무 커서 뭔가 잘못된게 아닌가하고 당황했습니다. 가령, '조국(국가)'과 '조국(후보자)'의 동음 이의어로 인해서 '조국(국가)' 내용의 노이즈가 섞여서 뉴스 기사 건수가 뻥튀기 된게 아닌가 하구요. 만약 Naver 뉴스 검색 기준이 '조국 법무부'로 검색하면 '조국 & 법무부'라면 '조국(국가)' 노이즈 기사가 포함되는 것을 막을 수 있을텐데요, '조국 or 법무부'로 혹시 뉴스 검색이 되는건 아닐까 노파심이 들어습니다. 그래서 하루치의 '조국 법무부' 키워드 뉴스 기사 검색 결과 약 2,500여개를 한페이지씩 넘겨가면서 눈으로 확인해봤습니다. '조국(사람 후보자' 기사 맞더군요. 그럼 이 포스팅의 숫자들 맞는겁니다. 


[ 조국 법무부 장관 후보자 최근 한달 뉴스 건수 ]


[ 황교안 전 법무부 장관 청문회 일주일 전까지, 한달간 뉴스 건수 ]


[ 고 노무현 대통령님 서거하시기 전 두달 뉴스 건수 ]


[ 각 부서 장관 후보자들 뉴스 건수 ]


728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 구분자(delimiter, separator)를 포함한 문자열 칼럼을 구분자를 기준으로 여러개의 칼럼으로 나누어서 DataFrame을 만드는 방법을 소개하겠습니다. 


그리고 PoestgreSQL, Greenplum DB에서도 구분자를 포함한 칼럼을 구분자를 기준으로 여러개의 칼럼으로 나누는 방법도 이어서 소개하겠습니다. 






 (1) pandas DataFrame 내 문자열 칼럼을 구분자로 분리하여 여러개의 칼럼 만들기


먼저 간단한 예를 들기 위해 ':' 구분자(delimiter, separator)를 가진 'col' 이라는 이름의 칼럼을 가진 pandas DataFrame을 만들어보겠습니다. 


import pandas as pd


df = pd.DataFrame({'col': ['a:1:20.3:S', 'b:2:10.5:C', 'c:3:51.9:A']})


df

col
0a:1:20.3:S
1b:2:10.5:C
2c:3:51.9:A




이제 원래의 'df' 라는 이름의 DataFrame 에 'col'변수를 그대로 둔 채로, ':' 구분자를 기준으로 'col' 문자열 칼럼을 분리(split) 하여 'group', 'id', 'value', 'grade' 라는 새로운 4개의 칼럼을 생성하여 추가해보겠습니다. split() 문자열 메소드는 split(separator, maxsplit) 의 형식으로 사용합니다. 



df[['group', 'id', 'value', 'grade']] = pd.DataFrame(df.col.str.split(':', 3).tolist())


df

colgroupidvaluegrade
0a:1:20.3:Sa120.3S
1b:2:10.5:Cb210.5C
2c:3:51.9:Ac351.9A

 



원래의 'col' 이름의 칼럼이 필요 없을 경우 원래의 DataFrame을 덮어쓰거나, 아니면 'col'을 포함하지 않는 새로운 DataFrame을 만들어주면 됩니다. 



df2 = pd.DataFrame(df.col.str.split(':', 3).tolist()

                   columns = ['group', 'id', 'value', 'grade'])


df2

groupidvaluegrade
0a120.3S
1b210.5C
2c351.9A

 



문자열(string)을 분리(split)해서 만든 새로운 칼럼들은 전부 문자열(string) 데이터 형식입니다. 이중에서 'id'와 'value' 칼럼을 숫자형(numeric)으로 변경하는 방법은 https://rfriend.tistory.com/470 포스팅을 참고하세요. 



df2.dtypes

group    object
id       object
value    object
grade    object
dtype: object

 





 (2) PostgreSQL, GPDB에서 문자열 칼럼을 구분자로 분리하여 여러개 칼럼 만들기


PostgreSQL, Greenplum DB에서는 split_part(string_column, separator, field_number) 의 형식으로 문자열 칼럼을 나눌 수 있습니다. 




위의 Python pandas DataFrame에서 사용했던 것과 동일한 예제 Table을 만들어서, 'col' 문자열 칼럼을 'group', 'id', value', 'grade'의 4개의 문자열(string)을 가진 새로운 Table을 만들어보겠습니다. 


-- make a table

DROP TABLE IF EXISTS grp_val_grade;

CREATE TABLE grp_val_grade (

col varchar(100) NOT NULL

);


INSERT INTO grp_val_grade VALUES ('a:1:20.3:S');

INSERT INTO grp_val_grade VALUES('b:2:10.5:C');

INSERT INTO grp_val_grade VALUES('c:3:51.9:A');


SELECT * FROM grp_val_grade; 





다음으로 split_part(string_column, separator, field_number) 함수를 사용해서 문자열 칼럼을 ':' 구분자를 기준으로 나누어서 새로운 칼럼을 만들어보겠습니다. 



-- split a column by delimeter and make 4 columns

DROP TABLE IF EXISTS grp_val_grade2;

CREATE TABLE grp_val_grade2 AS (

SELECT 

col

, split_part(col, ':', 1) AS group

, split_part(col, ':', 2) AS id 

, split_part(col, ':', 3) AS value

, split_part(col, ':', 4) AS grade

FROM grp_val_grade

);


SELECT * FROM grp_val_grade2;




많은 도움이 되었기를 바랍니다. 


이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾹 눌러주세요. :-)



728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 Python pandas DataFrame 이나 Series 내 문자열 칼럼을 숫자형으로 변환(how to convert string columns to numeric data types in pandas DataFrame, Series) 하는 2가지 방법을 소개하겠습니다. 


(1) pd.to_numeric() 함수를 이용한 문자열 칼럼의 숫자형 변환

(2) astype() 메소드를 이용한 문자열 칼럼의 숫자형 변환





  (1) pd.to_numeric() 함수를 이용한 문자열 칼럼의 숫자형 변환


(1-1) 한개의 문자열 칼럼을 숫자형으로 바꾸기



import numpy as np

import pandas as pd


# make a DataFrame

df = pd.DataFrame({'col_str': ['1', '2', '3', '4', '5']})

df

col_str
01
12
23
34
45


# check data types 

df.dtypes

col_str    object
dtype: object


df['col_int'] = pd.to_numeric(df['col_str'])

df

col_strcol_int
011
122
233
344
455


df.dtypes

col_str    object
col_int     int64
dtype: object





(1-2) apply() 함수와 to_numeric() 함수를 사용해 DataFrame 내 다수의 문자열 칼럼을 숫자형으로 바꾸기



# make a DataFrame with 3 string columns

df2 = pd.DataFrame({'col_str_1': ['1', '2', '3'], 

                   'col_str_2': ['4', '5', '6'], 

                   'col_str_3': ['7.0', '8.1', '9.2']})


df2

col_str_1col_str_2col_str_3
0147.0
1258.1
2369.2



df2.dtypes

col_str_1    object
col_str_2    object
col_str_3    object
dtype: object



# convert 'col_str_1' and 'col_str_2' to numeric

df2[['col_int_1', 'col_int_2']] = df2[['col_str_1', 'col_str_2']].apply(pd.to_numeric)

df2

col_str_1col_str_2col_str_3col_int_1col_int_2
0147.014
1258.125
2369.236



df2.dtypes

col_str_1    object
col_str_2    object
col_str_3    object
col_int_1     int64
col_int_2     int64
dtype: object



# convert all columns of a DataFrame to numeric using apply() and to_numeric together

df3 = df2.apply(pd.to_numeric)


df3.dtypes

col_str_1      int64
col_str_2      int64
col_str_3    float64
col_int_1      int64
col_int_2      int64
dtype: object






  (2) astype() 메소드를 이용한 문자열 칼럼의 숫자형 변환


(2-1) DataFrame 내 모든 문자열 칼럼을 float로 한꺼번에 변환하기



df4 = pd.DataFrame({'col_str_1': ['1', '2', '3'], 

                   'col_str_2': ['4.1', '5.5', '6.0']}) 


df4.dtypes

col_str_1    object
col_str_2    object
dtype: object



df5 = df4.astype(float)

df5

col_str_1col_str_2
01.04.1
12.05.5
23.06.0



df5.dtypes

col_str_1    float64
col_str_2    float64
dtype: object





(2-2) DataFrame 내 문자열 칼럼별로 int, float 데이터 형식 개별 지정해서 숫자형으로 변환하기



df6 = df4.astype({'col_str_1': int

                  'col_str_2': np.float})


df6

col_str_1col_str_2
014.1
125.5
236.0



 df6.dtypes

col_str_1      int64
col_str_2    float64
dtype: object






  DataFrame에 문자가 포함된 칼럼이 같이 있을 경우 ValueError


물론 DataFrame 내의 문자열 중에서 숫자가 아니라 문자(character)로 이루어진 문자열(string)이 포함되어 있을 경우 apply(pd.to_numeric) 함수나 DataFrame.astype(int) 메소드를 써서 한꺼번에 숫자형 데이터 형태로 변환하려고 하면 ValueError 가 발생합니다. (너무 당연한 거라서 여기에 써야 하나 싶기도 한데요... ^^;) 


이럴 때는 숫자만 들어있는 문자열 칼럼만을 선택해서 개별적으로 변환을 해주면 됩니다. 


아래는 문자로만 구성된 문자열 'col_2' 를 포함한 df7 데이터프레임을 만들어서 전체 칼럼을 숫자형으로 바꾸려고 했을 때 ValueError 가 발생한 예입니다. 



df7 = pd.DataFrame({'col_1': ['1', '2', '3'], 

                   'col_2': ['aaa', 'bbb', 'ccc']})


df7

col_1col_2
01aaa
12bbb
23ccc



df7.dtypes

col_1    object
col_2    object
dtype: object

 



* ValueError


 

# ValueError

df7 = df7.apply(pd.to_numeric)

--------------------------------------------------------------------------- ValueError Traceback (most recent call last) pandas/_libs/src/inference.pyx in pandas._libs.lib.maybe_convert_numeric() ValueError: Unable to parse string "aaa" During handling of the above exception, another exception occurred: -- 중간 생략 -- ~/anaconda3/lib/python3.6/site-packages/pandas/core/tools/numeric.py in to_numeric(arg, errors, downcast) 124 coerce_numeric = False if errors in ('ignore', 'raise') else True 125 values = lib.maybe_convert_numeric(values, set(), --> 126 coerce_numeric=coerce_numeric) 127 128 except Exception: pandas/_libs/src/inference.pyx in pandas._libs.lib.maybe_convert_numeric() ValueError: ('Unable to parse string "aaa" at position 0', 'occurred at index col_2')




* ValueError


# ValueError

df7 = df7.astype(int)

--------------------------------------------------------------------------- ValueError Traceback (most recent call last) <ipython-input-124-f50dad302c83> in <module>() ----> 1 df7 = df7.astype(int) -- 중간 생략 --

~/anaconda3/lib/python3.6/site-packages/pandas/core/dtypes/cast.py in astype_nansafe(arr, dtype, copy) 623 elif arr.dtype == np.object_ and np.issubdtype(dtype.type, np.integer): 624 # work around NumPy brokenness, #1987 --> 625 return lib.astype_intsafe(arr.ravel(), dtype).reshape(arr.shape) 626 627 if dtype.name in ("datetime64", "timedelta64"): pandas/_libs/lib.pyx in pandas._libs.lib.astype_intsafe() pandas/_libs/src/util.pxd in util.set_value_at_unsafe()  

ValueError: invalid literal for int() with base 10: 'aaa' 





 문자열을 숫자형으로 변환 시 ValueError 를 무시하기: df.apply(pd.to_numeric, errors = 'coerce') 



위의 예와는 조금 다르게 문자형을 숫자형으로 변환하려는 칼럼이 맞는데요, 값 중에 몇 개가 실수로 숫자로 된 문자열이 아니라 문자로 된 문자열이 몇 개 포함되어 있다고 해봅시다. 이럴 경우 문자열을 숫자로 파싱할 수 없다면서 ValueError가 발생하는데요, 문자가 포함되어 있는 경우는 강제로 'NaN'으 값으로 변환하고, 나머지 숫자로된 문자열은 숫자형으로 변환해주려면 errors = 'coerce' 옵션을 추가해주면 됩니다. 


 

df8 = pd.DataFrame({'col_1': ['1', '2', '3'], 

                   'col_2': ['4', 'bbb', '6']})


df8

col_1col_2
014
12bbb
236



df8 = df8.apply(pd.to_numeric)

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
pandas/_libs/src/inference.pyx in pandas._libs.lib.maybe_convert_numeric()

ValueError: Unable to parse string "bbb"

During handling of the above exception, another exception occurred:

ValueError                                Traceback (most recent call last)
<ipython-input-130-9e8d711c10d5> in <module>()
----> 1 df8 = df8.apply(pd.to_numeric)

~/anaconda3/lib/python3.6/site-packages/pandas/core/frame.py in apply(self, func, axis, broadcast, raw, reduce, args, **kwds)
   4260                         f, axis,
   4261                         reduce=reduce,
-> 4262                         ignore_failures=ignore_failures)
   4263             else:
   4264                 return self._apply_broadcast(f, axis)

~/anaconda3/lib/python3.6/site-packages/pandas/core/frame.py in _apply_standard(self, func, axis, ignore_failures, reduce)
   4356             try:
   4357                 for i, v in enumerate(series_gen):
-> 4358                     results[i] = func(v)
   4359                     keys.append(v.name)
   4360             except Exception as e:

~/anaconda3/lib/python3.6/site-packages/pandas/core/tools/numeric.py in to_numeric(arg, errors, downcast)
    124             coerce_numeric = False if errors in ('ignore', 'raise') else True
    125             values = lib.maybe_convert_numeric(values, set(),
--> 126                                                coerce_numeric=coerce_numeric)
    127 
    128     except Exception:

pandas/_libs/src/inference.pyx in pandas._libs.lib.maybe_convert_numeric()

ValueError: ('Unable to parse string "bbb" at position 1', 'occurred at index col_2')



df8 = df8.apply(pd.to_numeric, errors = 'coerce')

df8

col_1col_2
014.0
12NaN
236.0




많은 도움이 되었기를 바랍니다. 


이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾹 눌러주세요.  :-)



728x90
반응형
Posted by Rfriend
,

제가 2019년 1월 30일에 Pivotal 의 Data Science Webinar 에서 발표했었던 "오픈소스 그림플럼 DB와 아파치 MADlib을 활용한 그래프 분석, 네트워크 분석 (Graph analytics with Greenplum and Apache MADlib)" 자료를 공유합니다. 


[ Agenda ]

1. Why Graph Analytics?

2. What is Graph Analytics?

3. Graph Analytics with Apache MADlib on Greenplum in parallel


presented by Hongdon Lee, Pivotal Senior Data Scientist


[ 파일 첨부: Graph Analytics with Greenplum and Apache MADlib ]

Pivotal_Graph_Analytics_w_MADlib_GPDB_20190130.pdf




개별 단위별 객체, 대상만을 놓고 나누어 분석하는 환원주의(Reductionism: Divide and conquer) 대비 전체를 하나로 놓고 관계와 연결에 주목해서 분석하면 새로운 인사이트를 얻을 수 있다는 전체론 관점(Holism: Everything has to be understood in relation to the whole)의 분석 기법으로 그래프 이론(graph theory)네트워크 분석(network analysis)을 소개하는 자료입니다. 


그래프 분석 시 도전 사항으로 복잡도가 매우 높아 연산 부하가 크고 분석이 불가능하거나 시간이 오래 걸린다는 점입니다. 이를 해결할 수 있는 대안으로 MPP(Massively Parallel Processing) 아키텍처 기반의 오픈소스 Greenplum DBApache MADlib을 이용한 그래프, 네트워크 분석의 In-Database 병렬 처리 분석을 소개하였습니다. 


Webinar 발표할 때 재미를 더하기 위해서 슬라이드 중간 중간에 제 사진도 좀 넣어보았는데요, 이렇게 파일 공유하려니 좀 쑥스럽기도 하네요. ^^;  Webinar 발표할 때는 우리나라말로 했었는데요, 글로벌 데이터 팀 구성원들한테도 공유할 생각으로 슬라이드는 영어로 만들었습니다. 중간에 수식도 많은데 영어라서 눈에 잘 안들어온다는 피드백이 있었는데요, 양해 바랍니다. ^^;;; 


알고리즘 세부 소개하는 부분은 위키피디아를 많이 참고하였습니다. 


저는 실제 프로젝트 하면서 그래프 / 네트워크 분석이 제공할 수 있는 인사이트의 유용함을 경험하기도 했구요, Greenplum DB 에서 MADlib으로 분석하면서 In-DB parallel processing의 강력함을 경험했던 지라 기회되면 꼭 한번 소개를 하고 싶었던 주제였습니다. R이나 Python으로 노트북에서 network 분석 공부할 땐 아무 문제 없다가도, 실제 기업이나 공공기관의 수 terabyte, petabyte 급 데이터를 마주하게 되면 당황하기 마련인데요, 이럴 때 사용할 수 있는 방법, 툴입니다. 


주요 장표 몇 장만 아래에 화면 캡쳐한거 소개하자면요, 



  • Everything is connected!




  • Network: Everywhere with Everything, All the time



  • What is Graph Theory?



  • Graph algorithms and measures


  • Tools for Graph Analytics



  • Apache MADlib: Scaleable, In-Database Machine Learning in SQL



  • Apache MADlib: Graph Analytics Functions




그래프/ 네트워크 분석 주제 중에서 Page Rank 에 대한 간단한 예제 SQL 코드와 Graphviz, PyGraphviz를 활용한 네트워크 시각화 코드도 공유합니다. 



[ 그래프 분석 MADlib SQL codes ]

graph_pagerank_madlib.sql


----------------------------------------------

-- Graph Analytics with Greenplum and MADlib

----------------------------------------------


-- CREATE VERTEX TABLE

DROP TABLE IF EXISTS vertex;

CREATE TABLE vertex(

id INTEGER

) DISTRIBUTED RANDOMLY;


INSERT INTO vertex 

VALUES

(0), 

(1), 

(2), 

(3), 

(4),

(5),

(6);


select * from vertex


-- CREATE EDGE TABLE

DROP TABLE IF EXISTS edge;

CREATE TABLE edge(

src INTEGER,

dest INTEGER,

user_id INTEGER

)

DISTRIBUTED BY (user_id);


INSERT INTO edge 

VALUES 

(0, 1, 1), (0, 2, 1), -- user id 1

(0, 4, 1), (1, 2, 1),

(1, 3, 1), (2, 3, 1),

(2, 5, 1), (2, 6, 1),

(3, 0, 1), (4, 0, 1),

(5, 6, 1), (6, 3, 1),

(0, 1, 2), (0, 2, 2), -- user id 2

(0, 4, 2), (1, 2, 2),

(1, 3, 2), (2, 3, 2),

(3, 0, 2), (4, 0, 2),

(5, 6, 2), (6, 3, 2);


select * from edge;



-- (1) Compute the PageRank with All IDs

DROP TABLE IF EXISTS pagerank_out, pagerank_out_summary;

SELECT madlib.pagerank(

'vertex' -- Vertex table

, 'id' -- Vertex id column

, 'edge' -- Edge table

, 'src=src, dest=dest' -- Comma delimited string of edge arguments

, 'pagerank_out' -- Output table of RageRank

, NULL -- Default damping factor (0.85)

); 


SELECT * FROM pagerank_out ORDER BY pagerank DESC;



-- (2) Compute the PageRank of vertices associated with each user using the grouping feature

DROP TABLE IF EXISTS pagerank_gr_out, pagerank_gr_out_summary;

SELECT madlib.pagerank(

'vertex' -- Vertex table

, 'id' -- Vertex id column

, 'edge' -- Edge table

, 'src=src, dest=dest' -- Comma delimited string of edge arguments

, 'pagerank_gr_out' -- Output table of PageRank

, NULL -- Default damping factor (0.85)

, NULL -- Default max iterations (100)

, 0.00000001 -- Threshold

, 'user_id'); -- Grouping column name


SELECT * FROM pagerank_gr_out ORDER BY user_id, pagerank DESC;



-- (3) Personalized PageRank of vertices {2, 4}

DROP TABLE IF EXISTS pagerank_pers_out, pagerank_pers_out_summary;

SELECT madlib.pagerank(

'vertex' -- Vertex table

, 'id' -- Vertex id column

, 'edge' -- Edge table

, 'src=src, dest=dest' -- Comma delimited string of edge arguments

, 'pagerank_pers_out' -- Output table of PageRank

, NULL -- Default damping factor (0.85)

, NULL


-- Default max iterations (100)

, NULL -- Default Threshold (1/number of vertices*1000)

, NULL -- No Grouping

, '{2, 4}' -- Personalization vertices

);


SELECT * FROM pagerank_pers_out ORDER BY pagerank DESC;


SELECT * FROM pagerank_pers_out_summary;


 




[ Graphviz 활용한 네트워크 시각화 Python codes ]

NW_visualization.py





#!/usr/bin/env python2

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

"""

Created on Tuesday Jau 29 2019

@author: Hongdon Lee

"""

#%% network visualization using Graphviz

import numpy as np

import pandas as pd

import pygraphviz as pgv


def run_query(query):

     import pandas as pd

     import psycopg2 as pg


    # DB Connection

     conn = pg.connect(host='localhost',

                       port='5432', 

                       dbname='gpadmin', 

                       user='gpadmin', 

                       password='pivotal')


     # Get a DataFrame

     query_result = pd.read_sql(query, conn)

     conn.close()

     return query_result



#%% Network Edge Table

query = """

select a.*, b.pagerank 

    from edge a

    left outer join pagerank_out b on a.src = b.id

;

"""


edge_pagerank = run_query(query)


#%% PageRank Values

query = """

select * from pagerank_out;

"""


pagerank_out  = run_query(query)



#%% NW Visualization using Graphviz with different size proportional to PageRank


import pygraphviz as pgv


# Generating the output flow_graph with PyGraphviz

flow_graph = pgv.AGraph(strict=False, directed=True) # directed graph


# Flow Direction(Left to Right, or Top to Bottom)

flow_graph.graph_attr['rankdir'] = 'LR' # from Left to Right


# Node Shape

flow_graph.node_attr['shape'] = 'circle'


# Making node with different size proportional to PageRank

for i in range(len(pagerank_out)):

    label_text = str(pagerank_out.id[i]) + '\n(' + str(pagerank_out.pagerank[i].round(decimals=2)) + ')'

    node_width = pagerank_out.pagerank[i]*10

    node_height = pagerank_out.pagerank[i]*10

    

    flow_graph.add_node(str(pagerank_out.id[i]), 

                        label=label_text, 

                        **{'width': str(node_width), 

                           'height': str(node_height)})


# Adding edge with different color by user_id

colors = ['blue', 'red']

 

for i in range(len(edge_pagerank)):

    if edge_pagerank.user_id[i] == 1:

        color_text = colors[0]

    else:

        color_text = colors[1]

    

    flow_graph.add_edge(str(edge_pagerank.src[i])

                        , str(edge_pagerank.dest[i])

                        , color = color_text)


#----- Finally, Draw the Network Diagram using dot program :-)

flow_graph.draw("/Users/ihongdon/Documents/nw_diagram.png", prog='dot')

 



[ Docker image를 이용해서 Greenplum, MADlib, PL/R, PL/Python 분석 환경 구성하는 방법 (싱글 노드의 로컬 기능 테스트, 공부 용도로)]

https://rfriend.tistory.com/379 

https://hub.docker.com/r/hdlee2u/gpdb-analytics



많은 도움이 되었기를 바랍니다. 


ps. 앤 해서웨이(ANNE HATHAWAY)와 연결해주실 분 계신가요? 6 degrees of separation 의 기적이 저에게도 일어날 수 있으려나요? ^^" 



728x90
반응형
Posted by Rfriend
,

이번 포스팅에서는 Python pandas DataFrame의 칼럼 이름 바꾸는 방법(how to change column name in python pandas DataFrame), index 이름을 바꾸는 방법(how to change index name in python pandas DataFrame)을 소개하겠습니다. 

 

(1) pandas DataFrame의 칼럼 이름 바꾸기

    :  df.columns = ['a', 'b']

    :  df.rename(columns = {'old_nm' : 'new_nm'}, inplace = True)

(2) pandas DataFrame의 인덱스 이름 바꾸기

    : df.index = ['a', 'b']

    : df.rename(index = {'old_nm': 'new_nm'}, inplace = True)

 

 

 

  (1) Python pandas DataFrame 의 칼럼 이름 바꾸기

 

예제로 사용할 간단한 pandas DataFrame을 만들어보겠습니다. 

 

In [1]: import pandas as pd


In [2]: df = pd.DataFrame({'id': ['a', 'b', 'c', 'd'],
   ...: 'col_1': [1, 2, 3, 4],
   ...: 'col_2': [1, 1, 2, 2]},
   ...: columns = ['id', 'col_1', 'col_2'])


In [3]: df
Out[3]:
  id  col_1  col_2
0  a      1      1
1  b      2      1
2  c      3      2
3  d      4      2


In [4]: df.columns


Out[4]: Index(['id', 'col_1', 'col_2'], dtype='object')

 

 

 

(1-1) df.columns = ["new_1", "new_2"] 를 이용한 칼럼 이름 바꾸기

 

In [5]: df.columns = ["group", "val_1", "val_2"]


In [6]: df
Out[6]:
  group  val_1  val_2
0     a      1      1
1     b      2      1
2     c      3      2
3     d      4      2

 

 

df.columns 메소드를 사용해서 칼럼 이름을 변경하고자 하는 경우, DataFrame의 칼럼 개수 (number of columns in DataFrame)를 정확하게 일치시켜주어야 합니다. DataFrame의 칼럼 개수와 df.columns = [xx, xx, ...] 의 칼럼 개수가 서로 다를 경우 ValueError: Length mismatch 에러가 발생합니다. 

 

# need to match the number of columns
# ValueError: Length mismatch
In [7]: df.columns = ["group", "val_1"] # length mismatch error
   ...:
Traceback (most recent call last):


File "<ipython-input-7-5ab3ecd42fe8>", line 1, in <module>
df.columns = ["group", "val_1"]
... 중간 생략 ...


File "C:\Users\admin\Anaconda3\lib\site-packages\pandas\core\internals\managers.py", line 155, in set_axis
'values have elements'.format(old=old_len, new=new_len))


ValueError: Length mismatch: Expected axis has 3 elements, new values have 2 elements

 

 

 

(1-2) df.rename(columns = {"old_1": "new_1", "old_2": "new_2"}, inplace=True) 를 이용하여 칼럼 이름 변경하기

 

In [8]: df.rename(columns = {"id": "group",
   ...: "col_1": "val_1",
   ...: "col_2": "val_2"}, inplace = True)
   ...:
In [9]: df


Out[9]:
  group  val_1  val_2
0     a      1      1
1     b      2      1
2     c      3      2
3     d      4      2

 

 

df.columns 메소드와는 달리 df.rename(columns = {'old': 'new'}) 함수는 DataFrame의 칼럼 개수를 맞추어줄 필요가 없으며, 특정 칼럼 이름만 선별적으로 바꿀 수 있습니다. 아래 예제는 "group" 칼럼 이름을 "ID_2" 라는 새로운 칼럼 이름으로 바꾸어준 예입니다. 

In [10]: df.rename(columns = {"group": "ID_2"}, inplace = True)


In [11]: df
Out[11]:
  ID_2  val_1  val_2
0    a      1      1
1    b      2      1
2    c      3      2
3    d      4      2

 

 

lambda 함수를 사용하여서 기존 DataFrame의 칼럼 앞에 "X_" 라는 접두사(prefix)를 붙인 새로운 칼럼 이름을 만들어보겠습니다. 

 

In [14]: df = pd.DataFrame({'id': ['a', 'b', 'c', 'd'],
    ...: 'col_1': [1, 2, 3, 4],
    ...: 'col_2': [1, 1, 2, 2]},
    ...: columns = ['id', 'col_1', 'col_2'])


In [15]: df
Out[15]:
  id  col_1  col_2
0  a      1      1
1  b      2      1
2  c      3      2
3  d      4      2


In [16]: df.rename(columns = lambda x: "X_" + x, inplace = True)


In [17]: df
Out[17]:
  X_id  X_col_1  X_col_2
0    a        1        1
1    b        2        1
2    c        3        2
3    d        4        2

 

 

 

  (2) DataFrame의 Index 이름 바꾸기

 

(2-1) df.index = ['new_idx1', 'new_idx2'] 을 이용하여 Index 이름 바꾸기

 

이때 DataFrame의 index 개수와 바꾸고자 하는 index 이름의 개수를 서로 맞추어주어야 합니다. 

 

In [17]: df
Out[17]:
  X_id  X_col_1  X_col_2
0    a        1        1
1    b        2        1
2    c        3        2
3    d        4        2


In [18]: df.index
Out[18]: RangeIndex(start=0, stop=4, step=1)


In [19]: df.index = ['a', 'b', 'c', 'd']


In [20]: df
Out[20]:
  X_id  X_col_1  X_col_2
a    a        1        1
b    b        2        1
c    c        3        2
d    d        4        2

 

 

(2-2) df.rename(index = {'old_idx': 'new_idx'}, inplace = True) 를 이용한 index 이름 바꾸기

 

In [21]: df.rename(index = {0: 'a',
    ...: 1: 'b',
    ...: 2: 'c',
    ...: 3: 'd'}, inplace = True)


In [22]: df
Out[22]:
  X_id  X_col_1  X_col_2
a    a        1        1
b    b        2        1
c    c        3        2
d    d        4        2

 

 

pandas DataFrame의 칼럼 순서 변경하기는 https://rfriend.tistory.com/680 를 참고하세요. 

 

많은 도움이 되었기를 바랍니다. 

이번 포스팅이 도움이 되었다면 아래의 '공감~

'를 꾹 눌러주세요. :-)

 

728x90
반응형
Posted by Rfriend
,

Python 으로 프로그래밍을 하다보면 의도했던, 의도하지 않았던 간에 예외를 처리해야 하는 상황에 맞닥드리게 됩니다. 이때 에러를 발생(Raise Error) 시킬 수도 있고 회피할 수도 있으며, 필요에 따라서는 예외 처리를 해야 할 수도 있습니다. 


이번 포스팅에서는 Python에서 예외를 처리하는 4가지 try, except, else, finally 절(clause)을 소개하겠습니다. 




  • try 에는 정상적으로 실행할 프로그램 코드(code block) 써줍니다. 

  • except 절에는 의 try 절에서 실행한 코드에 예외가 발생했을 경우에 실행할 코드를 써줍니다. 

  • else 절에는 앞의 try 절에서 실행한 코드에 예외가 발생하지 않은 경우에 실행할 코드를 써줍니다. 

  • finally 절에는 try 절에서의 예외 발생 여부에 상관없이 항상(always execute) 마지막에 실행할 코드를 써줍니다. 


간단한 예로서, 두개의 숫자를 매개변수로 받아서 나눗셈을 하는 사용자 정의함수를 try, except, else, finally 의 예외절을 사용하여 정의해 보겠습니다. 


try, except, else, finally 절의 끝에는 콜론(:)을 붙여주며, 그 다음줄에 코드 블락은 들여쓰기(indentation)을 해줍니다. 

except의 경우 'except:' 만 사용해도 되고, 아래 예의 'except ZeorDivisionError as zero_div_err:' 처럼 Built-in Exception Class를 사용해서 에러를 명시적으로 써줄 수도 있습니다. (본문의 제일 밑에 Python Built-in Exception Class 의 계층 구조 참조)



# Python Exceptions: try, except, else, finally

def divide(x, y):

    try:

        result = x / y

    except ZeroDivisionError as zero_div_err:

        print("Except clause:", zero_div_err)

    else:

        print("Else clause: The result of division is", result)

    finally:

        print("Finally clause is executed.")

 




(1) try 절 정상 실행 시 (executed nomally): try --> else --> finally


두 숫자 x, y를 인자로 받아서 나눗셈을 하는 사용자 정의함수 divide(x, y) 에 x=1, y=2 를 넣어서 정상적으로 코드가 수행되었을 때의 결과를 보겠습니다.  마지막으로 finally 절이 실행되었습니다. 


In [1]: def divide(x, y):

   ...: try:

   ...: result = x / y

   ...: except ZeroDivisionError as zero_div_err:

   ...: print("Except clause:", zero_div_err)

   ...: else:

   ...: print("Else clause: The result of division is", result)

   ...: finally:

   ...: print("Finally clause is executed.")

   ...:

   ...:

In [2]: divide(1, 2)

Else clause: The result of division is 0.5

Finally clause is executed. 




(2) try 절 실행 중 예외 발생 시 (exception occurred): try --> except --> finally


1을 '0'으로 나누라고 하면 'except ZeroDivisionError as zero_div_err:' 의 except 절이 실행됩니다. 그리고 마지막으로 finally 절이 실행되었습니다. 


In [3]: divide(1, 0)

Except clause: division by zero

Finally clause is executed.




(3) try 절 Error 발생 시: try --> finally  --> Raise Error


divide(x, y) 사용자 정의함수에 숫자를 인자로 받는 매개변수에 문자열(string)을 입력하는 경우 TypeError가 발생하겠지요? 이때 finally를 먼저 실행하고, 그 후에 TypeError 를 발생시킵니다. 


In [4]: divide("1", "2")

Finally clause is executed.

Traceback (most recent call last):


File "<ipython-input-4-bbf78a5b43b9>", line 1, in <module>

divide("1", "2")


File "<ipython-input-1-78cbb56f9746>", line 3, in divide

result = x / y


TypeError: unsupported operand type(s) for /: 'str' and 'str'


Traceback (most recent call last):


File "<ipython-input-4-bbf78a5b43b9>", line 1, in <module>

divide("1", "2")


File "<ipython-input-1-78cbb56f9746>", line 3, in divide

result = x / y


TypeError: unsupported operand type(s) for /: 'str' and 'str' 




(4) Try Except 절에서 에러 발생 시 Exception Error 이름과 Error message 출력



import numpy as np


arr = np.array([[1., 2., 3.], [4.0, 5., 6.]])

print(arr)

[Out]
[[1. 2. 3.]
 [4. 5. 6.]]


# It will not work and eraise an error

try:

    arr.reshape(5, -1)

except Exception as e:

    print(f"{type(e).__name__}: {e}")

[Out] 
ValueError: cannot reshape array of size 6 into shape (5,newaxis)





[ 참고: The Class Hierarchy of Python Built-in Exceptions ]

(* source: https://docs.python.org/3/library/exceptions.html)


 BaseException

 +-- SystemExit

 +-- KeyboardInterrupt

 +-- GeneratorExit

 +-- Exception

      +-- StopIteration

      +-- StopAsyncIteration

      +-- ArithmeticError

      |    +-- FloatingPointError

      |    +-- OverflowError

      |    +-- ZeroDivisionError

      +-- AssertionError

      +-- AttributeError

      +-- BufferError

      +-- EOFError

      +-- ImportError

      |    +-- ModuleNotFoundError

      +-- LookupError

      |    +-- IndexError

      |    +-- KeyError

      +-- MemoryError

      +-- NameError

      |    +-- UnboundLocalError

      +-- OSError

      |    +-- BlockingIOError

      |    +-- ChildProcessError

      |    +-- ConnectionError

      |    |    +-- BrokenPipeError

      |    |    +-- ConnectionAbortedError

      |    |    +-- ConnectionRefusedError

      |    |    +-- ConnectionResetError

      |    +-- FileExistsError

      |    +-- FileNotFoundError

      |    +-- InterruptedError

      |    +-- IsADirectoryError

      |    +-- NotADirectoryError

      |    +-- PermissionError

      |    +-- ProcessLookupError

      |    +-- TimeoutError

      +-- ReferenceError

      +-- RuntimeError

      |    +-- NotImplementedError

      |    +-- RecursionError

      +-- SyntaxError

      |    +-- IndentationError

      |         +-- TabError

      +-- SystemError

      +-- TypeError

      +-- ValueError

      |    +-- UnicodeError

      |         +-- UnicodeDecodeError

      |         +-- UnicodeEncodeError

      |         +-- UnicodeTranslateError

      +-- Warning

           +-- DeprecationWarning

           +-- PendingDeprecationWarning

           +-- RuntimeWarning

           +-- SyntaxWarning

           +-- UserWarning

           +-- FutureWarning

           +-- ImportWarning

           +-- UnicodeWarning

           +-- BytesWarning

           +-- ResourceWarning



많은 도움이 되었기를 바랍니다. 

이번 포스팅이 도움이 되었다면 아래의 '공감~'를 꾹 눌러주세요. :-)



728x90
반응형
Posted by Rfriend
,