파이썬(python) 은 데이터 분석을 위한 가장 보편적인 프로그래밍 언어로 자리 잡았습니다. 데이터 분석을 위해
NumPy, Pandas, SciPy, scikit-learn 등 파이썬의 주요 패키지를 주로 사용합니다.
특히, 판다스(Pandas) 는 데이터 분석에 있어서 가장 많이 사용하는 피이썬 라이브러리 중 하나 입니다. 데이터를 분석할 때 NumPy(넘파이), Pandas(판다스)로 데이터를 가공한 후 분석 라이브러리인 statsmodels(통계 분석), scikit-learn(머신러닝)를 활용합니다.
판다스의 핵심인 데이터프레임(DataFrame) 은 행(Row) x 열(Column) 로 구성된 2차원의 표 형식 으로 데이터를 다루기 때문에 훨씬 더 쉽게 데이터를 핸들링 할 수 있습니다.
그럼, 엘라스틱서치에서도 데이터프레임(DataFrame)을 활용해 데이터 분석을 할 수 있을까요?
네, Eland를 통해서 가능합니다
Eland를 사용해 파이썬으로 어떻게 데이터 분석 및 머신 러닝에 활용할 수 있는지 소개하겠습니다.
Overview
Eland 는 오픈 소스 파이썬 라이브러리이며 데이터프레임(DataFrame) 과 머신러닝 을 위한 파이썬 클라이언트(Python client) 툴킷(toolkit) 입니다.
지원 및 권장하는 버전
- 파이썬 3.7+, Pandas 1.3
- Elasticsearch cluster 7.11+, 권장되는 버전은 7.14+
Why Eland?
Eland의 장점은 크게 3가지가 있습니다.
Eland 장점
- 데이터를 메모리에 보관하지 않고도 대용량의 데이터 셋을 탐색할 수 있다.
- 데이터를 Elasticsearch에 업로드할 수 있다.
- DSL Query를 사용하지 않고 보다 간편하게 검색 및 집계를 할 수 있다.
Data Frame
실습에 사용된 Elastic Stack, 주요 패키지 버전은 다음과 같습니다.
- Elastic Cloud: Deployment version 7.14.1
- 운영체제: macOS Big Sur (버전 11.6)
- 파이썬: 3.7.11
- Pandas: 1.3.2
- Eland: 8.0.0b1
- NumPy: 1.21.2
- matplotlib: 3.5.1
- seaborn: 0.11.2
1. Elasticsearch Cloud에서 Deployment 생성
- Elastic Cloud를 통해 Elastic Stack을 구성합니다. Elastic Cloud는 14일 동안 무료로 사용 할 수 있으며 신용카드 등록이 필요 없습니다.
2. Kibana에서 Eland API에 사용할 User를 생성
Kibana > Open
클릭합니다.
Managment > Stack Management > Security > Users > Create User
클릭합니다.
Username
과Password
는elastic
로 설정했습니다.
3. 데이터 추가
Kibana > Add data > Sample data > Sample flight data > Add data
Kibana > Management > Stack Management > Kibana > Index Patterns
를 보면 방금 추가한 데이터kibana_sample_data_flights
를 확인할 수 있습니다.
4. Authentication
- 먼저, 아래 두 값을 확인합니다.
- ELASTIC_CLOUD_ID: Cloud ID
- ELASTIC_CLOUD_PASSWORD: elastic
ELASTIC_CLOUD_ID
값은 여기서 확인할 수 있으며 ELASTIC_CLOUD_PASSWORD
는 이전의 만들었던 elastic
를 입력하면 됩니다. (예제이기 때문에 패스워드를 직접 입력했습니다. 운영 환경에서 사용시 직접 패스워드를 입력하는 방법이 아닌 다른 방법을 고려해야 합니다.)
5. Elasticsearch cluster 연결
- 아래의 코드를 작성한 뒤 실행시킵니다. 다른 패키지가 설치되어 있지 않다면
pip install 모듈
을 통해 설치를 진행합니다.
import eland as ed
import numpy as np
from elasticsearch import Elasticsearch
import matplotlib.pyplot as plt
def json(x):
import json
print(json.dumps(x, indent=2, sort_keys=True))
ELASTIC_CLOUD_ID = "Jihyun_Eland:YXNpYS1ub3J0aGVhc3QzLmdjcC5lbGFzdGljLWNsb3VkLmNvbSQyZjhjMjdkNmY4YjU0YWU3YTgxNzE0ZjQ1OGVhZDBmZSQxZTQwMjIxZjZkMjU0MjA****4N2Q0OTgzMjUyZQ=="
ELASTIC_CLOUD_PASSWORD = "elastic"
es = Elasticsearch(
cloud_id=ELASTIC_CLOUD_ID,
basic_auth=("elastic", ELASTIC_CLOUD_PASSWORD)
)
print(es.info())
- Elastic cloud 인스턴스에 정상적으로 연결되었습니다.
{'name': 'instance-0000000001', 'cluster_name': '2f8c27d6f8b54ae7a81714f458ead0fe', 'cluster_uuid': 'XYm66sAvRAymR6kL3zUu3A', 'version': {'number': '7.14.1', 'build_flavor': 'default', 'build_type': 'docker', 'build_hash': '66b55ebfa59c92c15db3f69a335d500018b3331e', 'build_date': '2021-08-26T09:01:05.390870785Z', 'build_snapshot': False, 'lucene_version': '8.9.0', 'minimum_wire_compatibility_version': '6.8.0', 'minimum_index_compatibility_version': '6.0.0-beta1'}, 'tagline': 'You Know, for Search'}
6. 데이터 프레임(DataFrame)
- 이제
eland
데이터 프레임(DataFrame)을 사용하여kibana_sample_data_flight
인덱스를 가져오겠습니다.
ed_flights = ed.DataFrame(es, es_index_pattern="kibana_sample_data_flights")
ed_flights.head()
- 행(Row) x 열(Column)로 구성된 2차원의 표 형식으로 손쉽게 데이터를 확인할 수 있습니다. 데이터 프레임과 엘라스틱서치의 구조를 비교해보면 아래와 같습니다.
Mapping Data Frame Concepts to Elasticsearch
- Data Frame -> Indices
- Row -> Document
- Column -> Field
- Dtype -> Field Type
- Index -> Doc ID + Sort Order
Eland 장점 1) 데이터를 메모리에 보관하지 않고도 대용량의 데이터 셋을 탐색
- info() 함수를 통해 Columns, rows, null 값, Dtype 등을 확인할 수 있습니다. 특히, memory usage를 살펴보면 96 bytes를 사용하고 있습니다.
ed_flights.info()
- Eland 데이터 프레임에 저장된
ed_flights
데이터셋을 csv 파일로 저장한 뒤 판다스(Pandas)read_csv()
사용해df_flights
데이터 프레임에 저장합니다. 그리고 메모리 사용량을 비교해보겠습니다.
ed_flights.to_csv("kibana_sample_data_flights_2.csv", index=False, mode='w')
df_flights = pd.read_csv("kibana_sample_data_flights_2.csv")
df_flights.info()
-
Eland가 훨씬 적은 메모리를 사용하고 있다는 것을 확인할 수 있습니다.
-
Eland API를 통해 모든 데이터와 작업은 Elasticsearch 클러스터에서 실행되고 결괏값은 데이터 프레임 형식으로 표현됩니다.
Eland 장점 2) 데이터를 Elasticsearch에 업로드
- 판다스 데이터 프레임을 Eland 데이터 프레임으로 변환하여 Elasticsearch에 인덱스로 저장할 수 있습니다. 데이터 프레임
df_flights
를 연결된 Elastics Cloud 인스턴스에kibana_sample_data_flights_2
로 저장해보겠습니다.
# Elastic cloud 인스턴스에 데이터 업로드
df = ed.pandas_to_eland(
pd_df=df_flights,
es_client=es,
# Elastic cloud 인스턴스에 저장될 인덱스
es_dest_index="kibana_sample_data_flights_2",
# 특정 컬럼에 대한 유형 재정의(여기서는 생략)
# es_type_overrides={
# },
# 인덱스가 존재하는 경우
es_if_exists="replace",
# 데이터가 인덱싱될 때까지 기다립니다.
es_refresh=True,
)
df.info()
7. 탐색적 데이터 분석(EDA)
- 탐색적 데이터 분석(EDA, Exploratory Data Analysis)을 해보겠습니다.
- 판다스에서 자주 사용하는 describe() 함수를 통해 평균, 표준표차, 최솟값, 최댓값 등 기술 통곗값을 확인할 수 있습니다.
ed_flights.describe().T
- 또한, 히스토그램을 통해서 데이터에 분포도 또한 살펴볼 수 있습니다.
ed_flights.select_dtypes(include=np.number).hist(figsize=[10,10])
plt.show()
8. Eland Pandas API vs Query DSL 비교
- 아래의 조건으로 Eland Pandas API와 Query DSL를 사용해 두 방법을 비교해보겠습니다.
ed_flights[(ed_flights.AvgTicketPrice > 900.0) & (ed_flights.Carrier == "Kibana Airlines")].head(10)
GET kibana_sample_data_flights/_search?size=10&sort=_doc:asc
{
"query": {
"bool" : {
"must": [
{"range": { "AvgTicketPrice": { "gt": 900.0 }}},
{"term": { "Carrier" : "Kibana Airlines"}}
]
}}
}
Eland 장점 3) DSL Query를 사용하지 않고 보다 간편하게 검색 및 집계를 할 수 있다.
- Query DSL에 익숙하지 않아도 판다스의 데이터 프레임 사용법을 조금만 익히면 손쉽게 데이터 검색 및 집계를 할 수 있습니다.
TIP: Eland에서 지원하지 않는 기능들은 Pandas로 변환해서 사용할 수 있습니다.
Machine Learning
- 머신러닝을 공부해 보신 분들은 다들 익숙한 붓꽃(Iris) 데이터 셋으로
eland.ml
에 대해 살펴보겠습니다.
[독립변수(X)]
- sepal length (cm): 꽃받침 길이
- sepal width (cm): 꽃받침 너비
- petal length (cm): 꽃잎 길이
- petal width (cm): 꽃잎 너비
[종속변수(y)]
- target: 꽃잎의 종류(Species), Virsicolor / Virginica / Setosa
1. 데이터 전처리
- 데이터 셋을 불러오고 붓꽃의 종류(Species)는 0인 경우 setosa, 1이면 versicolor, 그 외의 값인 virginica는 2로 분류해서 저장합니다.
# 데이터 셋
from sklearn import datasets
# 머신러닝을 위한 패키지
from sklearn.tree import DecisionTreeClassifier
from sklearn.model_selection import train_test_split
# 시각화를 위한 패키지
import seaborn as sns
# 경고 메시지 무시
import warnings
warnings.filterwarnings('ignore')
# iris dataset을 불러옵니다.
iris = datasets.load_iris()
df_iris = pd.DataFrame(data=np.c_[iris.data, iris.target], columns= iris.feature_names + ['target'])
# target을 분류
df_iris['target'] = df_iris['target'].apply(lambda x: 'setosa' if 0 == x else ('versicolor' if 1 == x else 'virginica'))
df_iris['target_number'] = iris.target
df_iris.head()
2. 시각화
- 꽃받침(sepal)의 길이와 너비를 시각화하여 붓꽃들이 어떻게 분류되어 있는지 살펴보겠습니다. 시각화를 위해 seaborn 패키지의 pairplot()을 사용합니다.
sns.pairplot(data=df_iris.loc[:, ['target', 'sepal length (cm)','sepal width (cm)']], hue="target", size=5,
plot_kws=dict(s=50, linewidth=1))
3. 머신러닝 학습
- 학습, 테스트 데이터로 분리하고, 테스트 데이터로 학습된 모델의 성능을 확인합니다.
data = iris.data
target = iris.target
# 학습, 테스트 데이터 분리
X_train, X_test, y_train, y_test = train_test_split(data, target, test_size = 0.2, random_state = 1)
# random_state은 동일한 결괏값을 얻기 위해 설정
iris_classifier = DecisionTreeClassifier(random_state = 1)
# 학습: fit()
iris_classifier.fit(X_train, y_train)
print("score : ", iris_classifier.score(X_test, y_test))
score : 0.9666666666666667
4. 학습된 모델 테스트
- 붓꽃의 종류(Species)별로 1개씩 데이터를 추출해 학습된 모델(
iris_classifier
)을 테스트해 보겠습니다.
df_iris.iloc[[1,70,120]]
from eland.ml import MLModel
# 학습된 모델을 적용
ed_classifier = MLModel.import_model(
es_client=es,
model_id="iris-classifier",
model=iris_classifier,
feature_names=iris.feature_names,
es_if_exists="replace"
)
# Elasticsearch API call 로깅
import logging
logger = logging.getLogger("elasticsearch")
logger.setLevel(logging.DEBUG)
logger.addHandler(logging.StreamHandler())
# 이전과 동일한 데이터를 사용하지만 Elasticsearch의 머신러닝 모델 사용
print(ed_classifier.predict(iris.data[[1,70,120]].tolist()))
print(iris.target[[1,70,120]])
logger.handlers = []
- 학습된 모델(
iris_classifier
)이 붓꽃의 종류를 정확히 예측한 것을 확인할 수 있습니다.
5. 결괏값 확인
- Elasticsearch API call 로깅한 결괏값을 확인해보겠습니다.
df_iris.iloc[[1,70,120]]
의 값을 확인할 수 있습니다.
json({"pipeline":{"processors":[{"inference":{"model_id":"iris-classifier","inference_config":{"classification":{}},"field_map":{}}}]},"docs":[{"_source":{"sepal length (cm)":4.9,"sepal width (cm)":3.0,"petal length (cm)":1.4,"petal width (cm)":0.2}},{"_source":{"sepal length (cm)":5.9,"sepal width (cm)":3.2,"petal length (cm)":4.8,"petal width (cm)":1.8}},{"_source":{"sepal length (cm)":6.9,"sepal width (cm)":3.2,"petal length (cm)":5.7,"petal width (cm)":2.3}}]})
- 학습된 모델
iris_classifier
이 예측한 값(predcted_value
)을 확인할 수 있습니다.
json({"docs":[{"doc":{"_index":"_index","_type":"_doc","_id":"_id","_source":{"sepal width (cm)":3.0,"petal width (cm)":0.2,"petal length (cm)":1.4,"sepal length (cm)":4.9,"ml":{"inference":{"prediction_score":1.0,"model_id":"iris-classifier","prediction_probability":1.0,"predicted_value":"0"}}},"_ingest":{"timestamp":"2021-12-16T23:45:47.841383931Z"}}},{"doc":{"_index":"_index","_type":"_doc","_id":"_id","_source":{"sepal width (cm)":3.2,"petal width (cm)":1.8,"petal length (cm)":4.8,"sepal length (cm)":5.9,"ml":{"inference":{"prediction_score":1.0,"model_id":"iris-classifier","prediction_probability":1.0,"predicted_value":"1"}}},"_ingest":{"timestamp":"2021-12-16T23:45:47.84139846Z"}}},{"doc":{"_index":"_index","_type":"_doc","_id":"_id","_source":{"sepal width (cm)":3.2,"petal width (cm)":2.3,"petal length (cm)":5.7,"sepal length (cm)":6.9,"ml":{"inference":{"prediction_score":1.0,"model_id":"iris-classifier","prediction_probability":1.0,"predicted_value":"2"}}},"_ingest":{"timestamp":"2021-12-16T23:45:47.841402925Z"}}}]})
지금까지 배운 내용을 요약하겠습니다.
- 데이터를 메모리에 보관하지 않고도 대용량에 데이터 셋을 탐색할 수 있다.
- 데이터를 Elasticsearch에 업로드할 수 있다.
- DSL Query를 사용하지 않고 보다 간편하게 검색 및 집계를 할 수 있다.
Eland는 현재 beta 버전이며 8.0 stable 버전을 내년 상반기 release를 준비 중입니다.