spark-shell을 이용해 Spark의 StringIndexer 예이다.




StringIndexer는 레이블의 문자열 컬럼을 레이블 인덱스의 컬럼으로 인코딩한다. 


인덱스는 [0, numLabels]안에 있고 레이블 빈도에 따라 정렬되므로 


가장 빈번한 레이블은 0번째 인덱스를 갖는다. 



입력 컬럼이 숫자라면 숫자를 문자열로 변경하고 문자열 값을 인덱싱한다. 


에스티메이터( 또는 트랜스포머와 같은 다운 스트림 파이프 라인 컴포넌트가 문자열로 구성된 인덱스 레이블을 사용하는 경우 컴포넌트의 입력 컬럼을 문자열로 구성된 인덱스 컬럼 이름으로 설정해야 한다. 



대부분의 경우 setInputCol을 사용해 입력 컬럼을 설정할 수 있다. 다음처럼 포맷의 여러 범주 데이터가 있다고 가정하자.



이제 가장 빈번한 이름(이 경우 Jason)이 0번째 인덱스을 갖도록 name 컬럼을 인덱싱하고 싶다고 가정하자. 간단한 데이터 프레임을 생성한다.


scala>


val df = spark.createDataFrame(

Seq((0, "Jason", "Germany"),

(1, "David", "France"),

(2, "Martin", "Spain"),

(3, "Jason", "USA"),

(4, "Daiel", "UK"),

(5, "Moahmed", "Bangladesh"),

(6, "David", "Ireland"),

(7, "Jason", "Netherlands"))).toDF("id", "name", "address")



이제 다음과 같이 name 컬럼을 인덱싱하자.


import org.apache.spark.ml.feature.StringIndexer


val indexer = new StringIndexer()

.setInputCol("name")

.setOutputCol("label")

.fit(df)





이제 다음과 같이 StringIndexer 인스턴스에 트랜스포머를 사용한다.


val indexed = indexer.transform(df)




이제 올바로 동작하는지 살펴보자.


scala>

indexed.show(false)


+---+-------+-----------+-----+

|id |name   |address    |label|

+---+-------+-----------+-----+

|0  |Jason  |Germany    |0.0  |

|1  |David  |France     |1.0  |

|2  |Martin |Spain      |3.0  |

|3  |Jason  |USA        |0.0  |

|4  |Daiel  |UK         |4.0  |

|5  |Moahmed|Bangladesh |2.0  |

|6  |David  |Ireland    |1.0  |

|7  |Jason  |Netherlands|0.0  |

+---+-------+-----------+-----+


그림 13 : StringIndexer를 사용한 레이블 생성



결과를 보면 name을 기반으로 label의 값이 같다.




원-핫(one-hot) 인코딩은 레이블 인덱스 컬럼을 이진 벡터 컬럼으로 매핑하며 최대 값은 단일 값이다. 원-핫 인코딩을 사용하면 분류 피쳐를 사용하기 위해 로지스틱 회귀(Logistic Regression)와 같은 연속적인 피쳐를 기대하는 알고리즘을 사용할 수 있다



val df = spark.createDataFrame(

Seq((0, Array("Jason", "David")),

(1, Array("David", "Martin")),

(2, Array("Martin", "Jason")),

(3, Array("Jason", "Daiel")),

(4, Array("Daiel", "Martin")),

(5, Array("Moahmed", "Jason")),

(6, Array("David", "David")),

(7, Array("Jason", "Martin")))).toDF("id", "name")

df.show(false)


이제 데이터셋에서 가장 빈번한 이름(이 경우는 Jason이다)이 0번째 인덱스를 갖도록 name 컬럼을 인덱싱할 것이다. 


그러나 name 컬럼을 인덱스를 사용하는 것은 무엇일까? 



즉 name 컬럼을 추가로 벡터화할 수 있다면 데이터 프레임을 모든 머신 러닝 모델로 쉽게 제공할 수 있다.



StringIndexer로 피팅한 것을 기반으로 데이터 프레임 예를 변환한다. 


import org.apache.spark.ml.feature.StringIndexer

import org.apache.spark.ml.feature.OneHotEncoder


val indexer = new StringIndexer()

                 .setInputCol("name")

                 .setOutputCol("categoryIndex")

                 .fit(df)


val indexed = indexer.transform(df)




결과는 다음과 같다. 


scala> 


indexed.show(false)

+---+-------+-----------+-------------+

|id |name   |address    |categoryIndex|

+---+-------+-----------+-------------+

|0  |Jason  |Germany    |0.0          |

|1  |David  |France         |1.0          |

|2  |Martin |Spain          |3.0          |

|3  |Jason  |USA            |0.0          |

|4  |Daiel  |UK                |4.0          |

|5  |Moahmed|Bangladesh |2.0          |

|6  |David  |Ireland          |1.0          |

|7  |Jason  |Netherlands |0.0          |

+---+-------+-----------+-------------+





OneHotEncoder 인스턴스를 생성한다.


import org.apache.spark.ml.feature.OneHotEncoder

val encoder = new OneHotEncoder()

                 .setInputCol("categoryIndex")

                 .setOutputCol("categoryVec")





이제 트랜스포머를 사용해 벡터로 변환한다.


scala>

val encoded = encoder.transform(indexed)

encoded.show()


+---+-------+-----------+-------------+-------------+

| id|   name|    address|categoryIndex|  categoryVec|

+---+-------+-----------+-------------+-------------+

|  0|  Jason|    Germany|          0.0|(4,[0],[1.0])|

|  1|  David|     France|              1.0|(4,[1],[1.0])|

|  2| Martin|      Spain|              3.0|(4,[3],[1.0])|

|  3|  Jason|        USA|              0.0|(4,[0],[1.0])|

|  4|  Daiel|         UK|                4.0|(4,[],[])|

|  5|Moahmed| Bangladesh|     2.0|(4,[2],[1.0])|

|  6|  David|    Ireland|              1.0|(4,[1],[1.0])|

|  7|  Jason|Netherlands|          0.0|(4,[0],[1.0])|

+---+-------+-----------+-------------+-------------+




이제 결과 데이터 프레임에 피쳐 벡터가 포함된 새로운 컬럼이 추가된 것을 볼 수 있다.

Posted by 김용환 '김용환'

댓글을 달아 주세요