# Dec 22nd, 2021 \[KO\] Eland: 데이터 분석을 위한 Python client

**URL:** <https://discuss.elastic.co/t/dec-22nd-2021-ko-eland-python-client/291954>\
**Category:** Advent Calendar\
**Created:** [December 22, 2021, 8:00am UTC](https://discuss.elastic.co/t/dec-22nd-2021-ko-eland-python-client/291954 "2021-12-22T08:00:48Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![Jihyun\_Jeong](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/jihyun_jeong/32/97416_2.png) [@Jihyun\_Jeong](https://discuss.elastic.co/u/Jihyun_Jeong)\
**Post date:** [December 22, 2021, 8:00am UTC](https://discuss.elastic.co/t/dec-22nd-2021-ko-eland-python-client/291954/1 "2021-12-22T08:00:48Z")

</div>

**파이썬(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) 입니다.

> ![image](https://us1.discourse-cdn.com/elastic/original/3X/7/5/7577fe6f0316a4bde5cc608ee4158a4edd30daac.png)

> **지원 및 권장하는 버전**
> 
> - 파이썬 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](https://www.elastic.co/kr/cloud/elasticsearch-service/signup?elektra=ess-cta1)를 통해 Elastic Stack을 구성합니다. Elastic Cloud는 14일 동안 무료로 사용 할 수 있으며 신용카드 등록이 필요 없습니다.

 ![image](https://us1.discourse-cdn.com/elastic/original/3X/e/c/ecfa135582efade4f9ac026f1a3823fcb485b869.png)

### 2. Kibana에서 Eland API에 사용할 User를 생성

- `Kibana > Open` 클릭합니다.

 ![image](https://us1.discourse-cdn.com/elastic/original/3X/2/b/2b70c03ab5355cbdae607df3c2af6c10d5d95d2a.png)

- `Managment > Stack Management > Security > Users > Create User` 클릭합니다.

 ![image](https://us1.discourse-cdn.com/elastic/original/3X/c/0/c0c1606ca8673f88db2f714bdcf3278feced375b.png)

- `Username`과 `Password`는 `elastic` 로 설정했습니다.

 ![image](https://us1.discourse-cdn.com/elastic/original/3X/7/a/7adb870ad9f3252cf08c3c855dc52a0318b72da4.png)

### 3. 데이터 추가

- `Kibana > Add data > Sample data > Sample flight data > Add data`

 ![image](https://us1.discourse-cdn.com/elastic/original/3X/4/d/4d9bedc27d8f9e110d26695b1c44c073fa3cf784.png)

 ![image](https://us1.discourse-cdn.com/elastic/original/3X/7/4/74cb3f9bf6486087a4cffb61d03909dd67958c1b.jpeg)

- `Kibana > Management > Stack Management > Kibana > Index Patterns`를 보면 방금 추가한 데이터 `kibana_sample_data_flights` 를 확인할 수 있습니다.

 ![image](https://us1.discourse-cdn.com/elastic/original/3X/b/8/b8a1d7ca567949ffd24c69233722ee7092899b3d.png)

### 4. Authentication

- 먼저, 아래 두 값을 확인합니다.

> - ELASTIC\_CLOUD\_ID: Cloud ID
> - ELASTIC\_CLOUD\_PASSWORD: elastic

`ELASTIC_CLOUD_ID` 값은 여기서 확인할 수 있으며 `ELASTIC_CLOUD_PASSWORD`는 이전의 만들었던 `elastic` 를 입력하면 됩니다. (예제이기 때문에 패스워드를 직접 입력했습니다. 운영 환경에서 사용시 직접 패스워드를 입력하는 방법이 아닌 다른 방법을 고려해야 합니다.)

 ![image](https://us1.discourse-cdn.com/elastic/original/3X/a/f/af0f7cde2a8da0934bc4aac7d1f583d49d2d079e.png)

### 5. Elasticsearch cluster 연결

- 아래의 코드를 작성한 뒤 실행시킵니다. 다른 패키지가 설치되어 있지 않다면 `pip install 모듈`을 통해 설치를 진행합니다.

```python
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 인스턴스에 정상적으로 연결되었습니다.

```json
{'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` 인덱스를 가져오겠습니다.

```auto
ed_flights = ed.DataFrame(es, es_index_pattern="kibana_sample_data_flights")
ed_flights.head()

```

> ![image](https://us1.discourse-cdn.com/elastic/original/3X/1/f/1fdbc9393ff47f0506cf466b0e04d49855295829.png)

- 행(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를 사용하고 있습니다.

```auto
ed_flights.info()

```

> ![image](https://us1.discourse-cdn.com/elastic/original/3X/7/6/763b40d58517447129d8ca15d52b2cf0bed7c820.png)

- Eland 데이터 프레임에 저장된 `ed_flights` 데이터셋을 csv 파일로 저장한 뒤 판다스(Pandas) `read_csv()` 사용해 `df_flights` 데이터 프레임에 저장합니다. 그리고 메모리 사용량을 비교해보겠습니다.

```python
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()

```

> ![image](https://us1.discourse-cdn.com/elastic/original/3X/0/2/02790c53f19f1e1dd1cf4d857bc7ecf381d4c4cf.png)

- Eland가 훨씬 적은 메모리를 사용하고 있다는 것을 확인할 수 있습니다.

- Eland API를 통해 모든 데이터와 작업은 Elasticsearch 클러스터에서 실행되고 결괏값은 데이터 프레임 형식으로 표현됩니다.

> **Eland 장점 2) 데이터를 Elasticsearch에 업로드**

- 판다스 데이터 프레임을 Eland 데이터 프레임으로 변환하여 Elasticsearch에 인덱스로 저장할 수 있습니다. 데이터 프레임 `df_flights`를 연결된 Elastics Cloud 인스턴스에 `kibana_sample_data_flights_2`로 저장해보겠습니다.

```auto
# 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()

```

> ![image](https://us1.discourse-cdn.com/elastic/original/3X/a/e/aea51743e47084f199f57161214bd2a794cb567e.png)

### 7. 탐색적 데이터 분석(EDA)

- 탐색적 데이터 분석(EDA, Exploratory Data Analysis)을 해보겠습니다.
- 판다스에서 자주 사용하는 describe() 함수를 통해 평균, 표준표차, 최솟값, 최댓값 등 기술 통곗값을 확인할 수 있습니다.

```auto
ed_flights.describe().T

```

> ![image](https://us1.discourse-cdn.com/elastic/original/3X/2/b/2bb97d984877b10f1b209d7d35b6cbab05709f18.png)

- 또한, 히스토그램을 통해서 데이터에 분포도 또한 살펴볼 수 있습니다.

```auto
ed_flights.select_dtypes(include=np.number).hist(figsize=[10,10])
plt.show()

```

> ![image](https://us1.discourse-cdn.com/elastic/original/3X/6/a/6a4d58ac3dfc301ce2d92978f254806b4dff0a57.jpeg)

### 8. Eland Pandas API vs Query DSL 비교

- 아래의 조건으로 Eland Pandas API와 Query DSL를 사용해 두 방법을 비교해보겠습니다.

```auto
ed_flights[(ed_flights.AvgTicketPrice > 900.0) & (ed_flights.Carrier == "Kibana Airlines")].head(10)

```

> ![image](https://us1.discourse-cdn.com/elastic/original/3X/a/a/aa0e20f39edf235ab20d415d16c7ab906d57468e.png)

```auto
GET kibana_sample_data_flights/_search?size=10&sort=_doc:asc
{
  "query": {
    "bool" : {
      "must": [
        {"range": { "AvgTicketPrice": { "gt": 900.0 }}},
        {"term": { "Carrier" : "Kibana Airlines"}}
        ]
    }}
}

```

> ![image](https://us1.discourse-cdn.com/elastic/original/3X/1/3/13ac18e03cac2a3e16890627035ea93d594b69c6.jpeg)

> **Eland 장점 3) DSL Query를 사용하지 않고 보다 간편하게 검색 및 집계를 할 수 있다.**

- Query DSL에 익숙하지 않아도 판다스의 데이터 프레임 사용법을 조금만 익히면 손쉽게 데이터 검색 및 집계를 할 수 있습니다.

> _ **TIP** : Eland에서 지원하지 않는 기능들은 Pandas로 변환해서 사용할 수 있습니다._
> 
> ![image](https://us1.discourse-cdn.com/elastic/original/3X/2/4/24c2359025418fcf64e057dfd17e81ad06080345.png)

# Machine Learning

- 머신러닝을 공부해 보신 분들은 다들 익숙한 [붓꽃(Iris) 데이터 셋](https://archive.ics.uci.edu/ml/datasets/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로 분류해서 저장합니다.

```auto
# 데이터 셋
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()

```

> ![image](https://us1.discourse-cdn.com/elastic/original/3X/1/b/1b221f915ee5239506d072d2789a2dc7e3f70190.png)

### 2. 시각화

- 꽃받침(sepal)의 길이와 너비를 시각화하여 붓꽃들이 어떻게 분류되어 있는지 살펴보겠습니다. 시각화를 위해 seaborn 패키지의 pairplot()을 사용합니다.

```python
sns.pairplot(data=df_iris.loc[:, ['target', 'sepal length (cm)','sepal width (cm)']], hue="target", size=5,
             plot_kws=dict(s=50, linewidth=1))

```

> ![image](https://us1.discourse-cdn.com/elastic/original/3X/3/b/3b8c0c1c0e7064f041b383ea51d93183bf2a6f6b.jpeg)

### 3. 머신러닝 학습

- 학습, 테스트 데이터로 분리하고, 테스트 데이터로 학습된 모델의 성능을 확인합니다.

```auto
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`)을 테스트해 보겠습니다.

```auto
df_iris.iloc[[1,70,120]]

```

> ![image](https://us1.discourse-cdn.com/elastic/original/3X/8/0/80ed2d8bb2dba8149c4e7d6f42476108091cbefb.png)

```auto
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 = []

```

 ![image](https://us1.discourse-cdn.com/elastic/original/3X/d/f/dfafe69c32fb87e70eeda610ad68e5e892fe391d.png)

- 학습된 모델(`iris_classifier`)이 붓꽃의 종류를 정확히 예측한 것을 확인할 수 있습니다.

### 5. 결괏값 확인

- Elasticsearch API call 로깅한 결괏값을 확인해보겠습니다. `df_iris.iloc[[1,70,120]]`의 값을 확인할 수 있습니다.

```auto
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}}]})

```

> ![image](https://us1.discourse-cdn.com/elastic/original/3X/3/d/3dfef1cacf0fae25a16ef926968b8847fc3f392d.png)

- 학습된 모델 `iris_classifier`이 예측한 값(`predcted_value`)을 확인할 수 있습니다.

```auto
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"}}}]})

```

> ![image](https://us1.discourse-cdn.com/elastic/original/3X/b/6/b685ef623455e778151c45045051c56ab508fc96.png)  
> ![image](https://us1.discourse-cdn.com/elastic/original/3X/0/6/06cd847408d4200b277529655d37943bbd21d702.png)

지금까지 배운 내용을 요약하겠습니다.

> - **데이터를 메모리에 보관하지 않고도 대용량에 데이터 셋을 탐색할 수 있다.**
> - **데이터를 Elasticsearch에 업로드할 수 있다.**
> - **DSL Query를 사용하지 않고 보다 간편하게 검색 및 집계를 할 수 있다.**

> ⚠ Eland는 현재 beta 버전이며 8.0 stable 버전을 내년 상반기 release를 준비 중입니다.

### Reference

- [Eland Python Client](https://www.elastic.co/guide/en/elasticsearch/client/eland/current/index.html)
- [Eland: DataFrames and Machine Learning backed by Elasticsearch](https://eland.readthedocs.io/en/latest/index.html)
- [Eland: A Python client for data analysis and exploration](https://www.elastic.co/elasticon/archive/2020/global/eland-a-python-client-for-data-analysis-and-exploration)
- [Introduction into Eland - DataFrames and Machine Learning backed by Elasticsearch](https://opsmatters.com/videos/introduction-eland-dataframes-and-machine-learning-backed-elasticsearch)

---

<div class="post-metadata">

**Author:** ![system](https://us1.discourse-cdn.com/elastic/original/3X/1/a/1ac57faf039f6b580b3f104ef42a2a89e41014de.png) [@system](https://discuss.elastic.co/u/system)\
**Post date:** [January 19, 2022, 8:01am UTC](https://discuss.elastic.co/t/dec-22nd-2021-ko-eland-python-client/291954/2 "2022-01-19T08:01:42Z")

</div>

This topic was automatically closed 28 days after the last reply. New replies are no longer allowed.
