data engineering

python create parquet format for hive, map type column.

qkqhxla1 2021. 6. 9. 21:59

1. make parquet file
spark를 사용하는 경우 dataframe.write.parquet처럼 간편하게 하면 되니 패스하고, raw python의 경우는 아래처럼 pandas를 사용해서 pandas dataframe으로 만든다음 parquet로 쓴다.
https://www.mikulskibartosz.name/how-to-write-parquet-file-in-python/를 참고한다.
pandas + pyarrow조합으로 아래처럼 리스트로 잘 구성해준다.

import pandas as pd
import pyarrow as pa
import pyarrow.parquet as pq

column_name = ['title', 'age', 'name', 'score']
datalist = [['title1', '11', 'name1', 90.8],
            ['title2', '20', 'name2', 40.5]]
df = pd.DataFrame(datalist, columns=column_name)
table = pa.Table.from_pandas(df)
parquet_path = './testparquet.parquet'
pq.write_table(table, parquet_path)
print(pd.read_parquet(parquet_path))  # parquet로 잘 써졌는지 확인용

글로 정리하다가 pandas dataframe, spark dataframe이 이름만 비슷한건지 궁금해서 더 찾아봤다.
좋은글 링크 1 : https://stackoverflow.com/questions/55912334/how-spark-dataframe-is-better-than-pandas-dataframe-in-performance
좋은글 링크 2 : https://towardsdatascience.com/parallelize-pandas-dataframe-computations-w-spark-dataframe-bba4c924487c
결론 : 이름만 같고 동작방식은 다르다, 데이터가 소규모일때는 pandas가 더 빠르다 대용량이 되면 spark가 더 빨라진다(너무 당연한 말인가), 스파크는 분산 시스템에서 동작하므로 parallel하게 돌릴수 있고 fault tolerance등이 있다. 등등..

2. create hive table.
위처럼 만든 parquet를 hdfs나 s3등에 올려준다. 나같은경우는 s3로 올렸다. 그리고 아래의 create external table를 실행시킨다. 
STORED AS PARQUET는 parquet포맷을 사용,
LOCATION 's3://my-s3/parquet_test'는 parquet의 위치이다.

CREATE EXTERNAL TABLE `db.parquet_test`(
title string,
age string,
name string,
score string
)
STORED AS PARQUET
LOCATION 's3://my-s3/parquet_test'


근데 생성은 잘 되는데 select를 해보면 에러가 뜬다. 엄청 길게 뜨는데 윗부분만 가져오자면..

하이브를 많이 안써봤으면 뭔소리야 저게 ㅡㅡ 하면서 에러 찾기도 힘들다. 근데 차근차근 읽어보면 중간에
Cannot inspect org.apache.hadoop.hive.serde2.io.DoubleWritable', sqlState=None ...이라고 DoubleWritable이라고 써있는걸 발견할수 있다. 아 맞다 내 필드중에 score는 소숫점이지??? 근데 create는 string으로 만들어서 충돌이 났다고 추측해볼수 있다. 
테이블을 드랍하고 score double로 변경후 다시 만들면 잘 나온다.
테이블을 만들때 내가 pandas dataframe으로 parquet를 만들때의 컬럼명과, 하이브 컬럼명이 다르면 잘 만들어지지 않는다.(깨진다.) csv로 만들었을때는 순서랑 타입만 맞으면 하이브 테이블 create시에 컬럼명을 다시 변경할수 있는데, parquet로 만들시에는 컬럼명이 동일하지 않으면 제대로 만들어지지 않는다.

하이브 테이블만들때 큰 데이터가 아니면 그냥 csv로 만들어서 올린다음 그거로 하이브 테이블을 만들었었다.(나중에 csv를 받아서 엑셀로 열어서 눈으로 확인하기도 편해서..) 근데 성능이 향상되는만큼 왠만해서 parquet로 전부 만들어서 올려야겠다. 
전에 https://qkqhxla1.tistory.com/1136 요글에서 하이브는 orc포맷이 좋다고 정리했었는데 스파크도 써서 그냥 parquet로 타협한다.

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

Hive에는 map이라는 타입이 있다. 파이썬의 딕셔너리같은 타입이며, create table시 컬럼 명을 map<string,int> 처럼 map<키, 값>형태로 만든다.  동작하는 예시를 한번 간단하게 보자 : https://stackoverflow.com/questions/14514669/map-type-variable-in-hive
이걸 적은 이유는 위에서 hive table용 parquet를 만들었는데.. pure python pandas + pyarrow으로는 hive 의 map컬럼에 맞는 형태로 parquet를 못 만드는것 같아서이다.

hive에 map<string, string>형태로 컬럼을 생성하고 parquet를 만들때는 파이썬 딕셔너리를 넣어보는 등 여러가지 해봤으나.. 동작하지 않았다. 진짜 여기저기 다찾아봤는데 
string을 map으로 변환시키는 함수나 : https://www.programmersought.com/article/57463619196/
csvfile을 map형태로 만드는 방법밖에 없었다.
여기저기 더 찾아봤는데 나랑 비슷한 고민을 한 사람이 있다 : https://github.com/dask/fastparquet/issues/180
이사람도 파이썬에서 fastparquet를 사용해서 hive map 타입을 만들려고 하는데 json만 된다고 했다. 그리고 저 글을 읽다보니 map이라는게 단순히 hive의 간단한 타입 중 하나가 아닌 좀더 복잡한 개념인것 같다..

어쨌든 돌아다니다가 csvfile을 map형태로 쓰는법을 찾았다 : https://chase-seibert.github.io/blog/2013/05/17/hive-insert-and-dump-csv-with-map-datatype.html
내 잠정적으로 스파크같은걸 사용하지 않는 pure python + 모듈로는 parquet을 만들었을때 hive의 map타입을 못 만드는거로 결론이 났고, csvfile로 구성하기로 했다.

create table시 아래 구문을 붙여준다.

CREATE EXTERNAL TABLE IF NOT EXISTS db.mytable
(a STRING
, b map<STRING, STRING>
)
PARTITIONED BY (dt STRING)
row format delimited  -- 여기부터..
fields terminated by '\t'
COLLECTION ITEMS TERMINATED BY ';'
MAP KEYS TERMINATED BY '=' --  여기까지가 csv나누기 + map타입을 위한거다.
STORED AS TEXTFILE LOCATION 's3://s3-abcde/test'
tblproperties("skip.header.line.count"="1"); 

COLLECTION ITEMS TERMINATED BY ';'   와
MAP KEYS TERMINATED BY '='  가 중점이다.

내가 b컬럼에 데이터를 string으로 'a=b;c=d;e=f' 이런식으로 넣어주면.. 내가 설정한 delimiter로 인해 값들이 나눠져서 사전으로 잘 들어간다. 이렇게 들어간 값들은 파이썬 사전을 호출하듯이
select b['a'] from db.mytable; 요런식으로 쿼리를 날려보면 b컬럼에서 a키값이 있는 값들이 출력된다.