Skip to content

Commit

Permalink
[SYSTEMDS-3184] Builtin for computing information gain using entropy …
Browse files Browse the repository at this point in the history
…and gini

Closes #1520
  • Loading branch information
morf1us authored Feb 12, 2022
1 parent 2fffa15 commit 7c3cc82
Show file tree
Hide file tree
Showing 5 changed files with 471 additions and 0 deletions.
45 changes: 45 additions & 0 deletions docs/site/builtins-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ limitations under the License.
* [`img_brightness`-Function](#img_brightness-function)
* [`img_crop`-Function](#img_crop-function)
* [`img_mirror`-Function](#img_mirror-function)
* [`impurityMeasures`-Function](#impurityMeasures-function)
* [`imputeByFD`-Function](#imputeByFD-function)
* [`intersect`-Function](#intersect-function)
* [`KMeans`-Function](#KMeans-function)
Expand Down Expand Up @@ -1020,6 +1021,50 @@ B = img_mirror(img_in = A, horizontal_axis = TRUE)
```


## `impurityMeasures`-Function

`impurityMeasures()` computes the measure of impurity for each feature of the given dataset based on the passed method (gini or entropy).

### Usage

```r
IM = impurityMeasures(X = X, Y = Y, R = R, n_bins = 20, method = "gini");
```

### Arguments

| Name | Type | Default | Description |
| :--------- | :-------------- | :------ | :---------- |
| X | Matrix[Double] | --- | Feature matrix X |
| Y | Matrix[Double] | --- | Target vector Y containing only 0 or 1 values |
| R | Matrix[Double] | --- | Row vector R indicating whether a feature is categorical or continuous. 1 denotes a continuous feature, 2 denotes a categorical feature. |
| n_bins | Integer | `20` | Number of equi-width bins for binning in case of scale features. |
| method | String | --- | String indicating the method to use; either "entropy" or "gini". |

### Returns

| Name | Type | Description |
| :--- | :------------- | :---------- |
| IM | Matrix[Double] | (1 x ncol(X)) row vector containing information/gini gain for each feature of the dataset. In case of gini, the values denote the gini gains, i.e. how much impurity was removed with the respective split. The higher the value, the better the split. In case of entropy, the values denote the information gain, i.e. how much entropy was removed. The higher the information gain, the better the split. |

### Example

```r
X = matrix("4.0 3.0 2.8 3.5
2.4 1.0 3.4 2.9
1.1 1.0 4.9 3.4
5.0 2.0 1.4 1.8
1.1 3.0 1.0 1.9", rows=5, cols=4)
Y = matrix("1.0
0.0
0.0
1.0
0.0", rows=5, cols=1)
R = matrix("1.0 2.0 1.0 1.0", rows=1, cols=4)
IM = impurityMeasures(X = X, Y = Y, R = R, method = "entropy")
```


## `imputeByFD`-Function

The `imputeByFD`-function imputes missing values from observed values (if exist)
Expand Down
139 changes: 139 additions & 0 deletions scripts/builtin/impurityMeasures.dml
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
#-------------------------------------------------------------
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
#-------------------------------------------------------------

# This function computes the measure of impurity for the given dataset based on the passed method (gini or entropy).
# The current version expects the target vector to contain only 0 or 1 values.
#
# INPUT PARAMETERS:
# ----------------------------------------------------------------------------------------------------------------------
# NAME TYPE DEFAULT MEANING
# ----------------------------------------------------------------------------------------------------------------------
# X Matrix[Double] --- Feature matrix.
# Y Matrix[Double] --- Target vector containing 0 and 1 values.
# R Matrix[Double] --- Vector indicating whether a feature is categorical or continuous.
# 1 denotes a continuous feature, 2 denotes a categorical feature.
# n_bins Integer 20 Number of bins for binning in case of scale features.
# method String --- String indicating the method to use; either "entropy" or "gini".
# ----------------------------------------------------------------------------------------------------------------------

# Output(s)
# ----------------------------------------------------------------------------------------------------------------------
# NAME TYPE DEFAULT MEANING
# ----------------------------------------------------------------------------------------------------------------------
# IM Matrix[Double] --- (1 x ncol(X)) row vector containing information/gini gain for
# each feature of the dataset.
# In case of gini, the values denote the gini gains, i.e. how much
# impurity was removed with the respective split. The higher the
# value, the better the split.
# In case of entropy, the values denote the information gain, i.e.
# how much entropy was removed. The higher the information gain,
# the better the split.
# ----------------------------------------------------------------------------------------------------------------------

m_impurityMeasures = function(Matrix[Double] X, Matrix[Double] Y, Matrix[Double] R, Integer n_bins = 20, String method)
return (Matrix[Double] IM)
{
if (method != "entropy" & method != "gini") {
stop("Please specify the correct method - should be either entropy or gini.")
}

IM = matrix(0.0, rows = 1, cols = ncol(X))

parfor (i in 1:ncol(X)) {
if (as.scalar(R[,i]) == 1) {
binned_feature = applyBinning(X[,i], n_bins)
IM[,i] = getImpurityMeasure(binned_feature, Y, n_bins, method)
} else {
IM[,i] = getImpurityMeasure(X[,i], Y, max(X[,i]), method)
}
}
}

getImpurityMeasure = function(Matrix[Double] feature, Matrix[Double] Y, Double max_cat, String method)
return (Double gain)
{
n_true_labels = sum(Y)
n_false_labels = length(Y) - n_true_labels
parent_impurity = calcImpurity(n_true_labels, n_false_labels, length(feature), method)

# calculate the impurity after the split
children_impurity = 0
for (i in 1:max_cat) {
count_true = 0
count_false = 0
for (j in 1:length(feature)) {
if (as.scalar(feature[j,]) == i) {
if (as.scalar(Y[j,]) == 0) {
count_false += 1
} else {
count_true += 1
}
}
}
if (!(count_true == 0 & count_false == 0)) {
children_impurity = children_impurity + calcImpurity(count_true, count_false, length(feature), method)
}
}
gain = parent_impurity - children_impurity
}

calcImpurity = function(Double n_true, Double n_false, Double n_vars, String method)
return (Double impurity)
{
impurity = 0
prob_true = n_true / (n_true + n_false)
prob_false = n_false / (n_true + n_false)
weight = (n_true + n_false) / n_vars

if (prob_true != 1 & prob_false != 1) { # if there is more than one class, calculate new impurity according to method.
if (method == "entropy") { # dividing by log(2) to obtain the information gain in bits
impurity = (-1) * weight * (prob_true * log(prob_true)/log(2) + prob_false * log(prob_false)/log(2))
} else if (method == "gini") {
impurity = weight * (1 - (prob_true^2 + prob_false^2))
}
}
}

applyBinning = function(Matrix[Double] feature, Double n_bins)
return (Matrix[Double] output_f)
{
# equi-width binning.

if (length(feature) < n_bins) {
n_bins = length(feature)
}
max_v = max(feature)
min_v = min(feature)
width = (max_v - min_v) / n_bins
output_f = matrix(1, rows = nrow(feature), cols = 1)

parfor (i in 1:length(feature)) {
binned = FALSE
j = 1
while (binned == FALSE) {
if (as.scalar(feature[i,]) <= min_v + j * width) {
output_f[i,] = j
binned = TRUE
}
j += 1
}
}
}
1 change: 1 addition & 0 deletions src/main/java/org/apache/sysds/common/Builtins.java
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ public enum Builtins {
IMG_SAMPLE_PAIRING("img_sample_pairing", true),
IMG_INVERT("img_invert", true),
IMG_POSTERIZE("img_posterize", true),
IMPURITY_MEASURES("impurityMeasures", true),
IMPUTE_BY_MEAN("imputeByMean", true),
IMPUTE_BY_MEAN_APPLY("imputeByMeanApply", true),
IMPUTE_BY_MEDIAN("imputeByMedian", true),
Expand Down
Loading

0 comments on commit 7c3cc82

Please sign in to comment.