Data Processing
Machine learning is all about building models from data. However, data scientists frequently talk about models and algorithms first, which very likely generates suboptimal results. The other approach is to play with the data first. Even simple statistics and plots can help us get feelings of data and problems, which more likely lead us to better modelling.
Features
A feature is an individual measurable property of an observation — also called
an explanatory variable, predictor, or regressor. Choosing informative,
discriminating features matters as much as choosing the algorithm. In SMILE,
tabular features live in a DataFrame
(or a plain double[][] / vector). For modeling, the
Formula API selects and encodes columns;
reusable numeric transforms such as standardization live under
smile.feature.transform (see the
Tutorial).
Feature engineering still needs domain knowledge: combine automated transforms with what you know about the problem. This page focuses on holding and shaping data; the Tutorial continues into Formula-based modeling.
Data Type
Statistically, attributes are qualitative (categories) or quantitative (counts and measurements), and may be measured on a nominal, ordinal, interval, or ratio scale. SMILE maps that vocabulary onto a typed schema:
DataTypes/StructType:Physical column types (
DoubleType,IntType,StringType, date/time, arrays, …). A DataFrame’sschema()is aStructTypeofStructFields.Measure:How values should be interpreted for learning and plotting.
NominalScaleandOrdinalScaleattach category labels to integral codes; numeric columns are typically continuous (interval/ratio).ValueVector.nominal/ordinal:Build factorized categorical columns efficiently. Plain
ValueVector.of(String…)keeps aStringVectorfor text-oriented work.
Many algorithms expect numeric matrices; trees and Formula-based workflows can
consume nominal fields more directly. Dates are first-class for indexing and
plots; strings are useful for NLP after further encoding. When you need an
explicit CSV schema, you will see StructType and
NominalScale again in the CSV section.
DataFrame
While some SMILE algorithms take simple double[] as input, we often use
the encapsulation class DataFrame.
DataFrame is a two-dimensional data structure like a table with rows
and columns. Each column is a ValueVector
that is a one-dimensional labeled abstraction to store a sequence of values having the same type.
Columns in a DataFrame may have different data types. There are concrete subclasses of ValueVector
for each primitive data type and generic object types. Creating a ValueVector by passing a list of values.
import java.time.*
import smile.data.*
import smile.data.vector.*
ValueVector.of("A", 1.0, 2.0, 3.0)
ValueVector.of("B", Instant.now())
ValueVector.nominal("C", "test", "train", "test", "train")
ValueVector.of("D",
"this is a string vector",
"Nominal/ordinal vectors store data as integers internally")
ObjectVector.of("E", Index.range(0, 4).toArray(), new int[]{3, 3, 3, 3})
A[1, 2, 3]
B[2025-01-18T23:08:54.375073600Z]
C[test, train, test, train]
D[this is a string vector, Nominal/ordinal vectors store data as integers internally]
E[[0, 1, 2, 3], [3, 3, 3, 3]]
import java.time._
import smile.data._
import smile.data.vector._
ValueVector.of("A", 1.0, 2.0, 3.0)
ValueVector.of("B", Instant.now())
ValueVector.nominal("C", "test", "train", "test", "train")
ValueVector.of("D",
"this is a string vector",
"Nominal/ordinal vectors store data as integers internally")
ObjectVector.of("E", Index.range(0, 4).toArray(), Array(3, 3, 3, 3))
A[1, 2, 3]
B[2025-01-19T01:58:06.813727400Z]
C[test, train, test, train]
D[this is a string vector, Nominal/ordinal vectors store data as integers internally]
E[[0, 1, 2, 3], [3, 3, 3, 3]]
import java.time.*
import smile.data.*
import smile.data.vector.*
import smile.util.*
ValueVector.of("A", 1.0, 2.0, 3.0)
ValueVector.of("B", Instant.now())
ValueVector.nominal("C", "test", "train", "test", "train")
ValueVector.of("D",
"this is a string vector",
"Nominal/ordinal vectors store data as integers internally")
ObjectVector.of("E", Index.range(0, 4).toArray(), intArrayOf(3, 3, 3, 3))
A[1, 2, 3]
B[2025-01-19T01:58:06.813727400Z]
C[test, train, test, train]
D[this is a string vector, Nominal/ordinal vectors store data as integers internally]
E[[0, 1, 2, 3], [3, 3, 3, 3]]
Note that the nominal and ordinal methods
factorize string values and store them as integral values internally,
which are more efficient, compact, and friendly for machine learning
algorithm. In contrast, the ValueVector.of(String...)
method returns a StringVector that store string values
as is, which is useful for text processing.
Creation
For illustration, create a DataFrame from a 2-dimensional array (default column
names V1, V2, …) or from typed columns. Call
schema() to inspect field names, types, and nullability.
var df = DataFrame.of(MathEx.randn(6, 4))
var mixed = new DataFrame(
ValueVector.of("A", 1.0),
ValueVector.of("B", LocalDate.parse("2013-01-02")),
ValueVector.of("C", "foo"),
ObjectVector.of("D", Index.range(0, 4).toArray()),
ObjectVector.of("E", new int[]{3, 3, 3, 3})
)
mixed.schema()
+---+---------+---------+---------+---------+
| | V1| V2| V3| V4|
+---+---------+---------+---------+---------+
| 0|-0.201469| 0.970363| 2.726932|-0.146014|
| 1| 1.872161| 0.495932| 0.553859|-0.028237|
| 2|-0.504866|-0.179409| 0.201377| 0.281267|
| 3| 0.894446| 0.791521| 0.053346| 0.213519|
| 4| 0.200011|-0.203736|-0.349196|-1.193759|
| 5| 1.52529|-1.407597| 1.16758| -1.78291|
+---+---------+---------+---------+---------+
+---+---+----------+---+------------+------------+
| | A| B| C| D| E|
+---+---+----------+---+------------+------------+
| 0| 1|2013-01-02|foo|[0, 1, 2, 3]|[3, 3, 3, 3]|
+---+---+----------+---+------------+------------+
{
A: double NOT NULL,
B: Date,
C: String,
D: int[],
E: int[]
}
We can create a DataFrame with a collection of records or beans.
enum Gender {Male, Female}
record Person(String name, Gender gender, String state, LocalDate birthday, int age, Double salary) { }
List<Person> persons = new ArrayList<>();
persons.add(new Person("Alex", Gender.Male, "NY", LocalDate.of(1980, 10, 1), 38, 10000.));
persons.add(new Person("Bob", Gender.Male, "AZ", LocalDate.of(1995, 3, 4), 23, null));
persons.add(new Person("Jane", Gender.Female, "CA", LocalDate.of(1970, 3, 1), 48, 230000.));
persons.add(new Person("Amy", Gender.Female, "NY", LocalDate.of(2005, 12, 10), 13, null));
var df = DataFrame.of(Person.class, persons);
+---+----+------+-----+----------+---+------+
| |name|gender|state| birthday|age|salary|
+---+----+------+-----+----------+---+------+
| 0|Alex| Male| NY|1980-10-01| 38| 10000|
| 1| Bob| Male| AZ|1995-03-04| 23| null|
| 2|Jane|Female| CA|1970-03-01| 48|230000|
| 3| Amy|Female| NY|2005-12-10| 13| null|
+---+----+------+-----+----------+---+------+
In above example, the column 'state' is of string type. Apparently, it is a categorical variable. We can factorize such string columns to categorical values:
var df2 = df.factorize("state");
df.get(0, 2)
df2.get(0, 2)
+---+----+------+-----+----------+---+------+
| |name|gender|state| birthday|age|salary|
+---+----+------+-----+----------+---+------+
| 0|Alex| Male| NY|1980-10-01| 38| 10000|
| 1| Bob| Male| AZ|1995-03-04| 23| null|
| 2|Jane|Female| CA|1970-03-01| 48|230000|
| 3| Amy|Female| NY|2005-12-10| 13| null|
+---+----+------+-----+----------+---+------+
"NY"
2
On the surface, everything seems same. But the column 'state' is actually converted to integral values under the hood.
Most SMILE parsers return a DataFrame. The snippet below is a quick preview of ARFF I/O; see Data Import and Export for each format in detail.
import smile.data.*
import smile.datasets.*
import smile.io.*
var iris = Read.arff("data/weka/iris.arff")
+---+-----------+----------+-----------+----------+-----------+
| |sepallength|sepalwidth|petallength|petalwidth| class|
+---+-----------+----------+-----------+----------+-----------+
| 0| 5.1| 3.5| 1.4| 0.2|Iris-setosa|
| 1| 4.9| 3| 1.4| 0.2|Iris-setosa|
| 2| 4.7| 3.2| 1.3| 0.2|Iris-setosa|
| 3| 4.6| 3.1| 1.5| 0.2|Iris-setosa|
| 4| 5| 3.6| 1.4| 0.2|Iris-setosa|
| 5| 5.4| 3.9| 1.7| 0.4|Iris-setosa|
| 6| 4.6| 3.4| 1.4| 0.3|Iris-setosa|
| 7| 5| 3.4| 1.5| 0.2|Iris-setosa|
| 8| 4.4| 2.9| 1.4| 0.2|Iris-setosa|
| 9| 4.9| 3.1| 1.5| 0.1|Iris-setosa|
+---+-----------+----------+-----------+----------+-----------+
140 more rows...
val iris = read.arff("data/weka/iris.arff")
iris: DataFrame =
+---+-----------+----------+-----------+----------+-----------+
| |sepallength|sepalwidth|petallength|petalwidth| class|
+---+-----------+----------+-----------+----------+-----------+
| 0| 5.1| 3.5| 1.4| 0.2|Iris-setosa|
| 1| 4.9| 3| 1.4| 0.2|Iris-setosa|
| 2| 4.7| 3.2| 1.3| 0.2|Iris-setosa|
| 3| 4.6| 3.1| 1.5| 0.2|Iris-setosa|
| 4| 5| 3.6| 1.4| 0.2|Iris-setosa|
| 5| 5.4| 3.9| 1.7| 0.4|Iris-setosa|
| 6| 4.6| 3.4| 1.4| 0.3|Iris-setosa|
| 7| 5| 3.4| 1.5| 0.2|Iris-setosa|
| 8| 4.4| 2.9| 1.4| 0.2|Iris-setosa|
| 9| 4.9| 3.1| 1.5| 0.1|Iris-setosa|
+---+-----------+----------+-----------+----------+-----------+
140 more rows...
import smile.*
import smile.data.*
import smile.io.*
val iris = Read.arff("data/weka/iris.arff")
+---+-----------+----------+-----------+----------+-----------+
| |sepallength|sepalwidth|petallength|petalwidth| class|
+---+-----------+----------+-----------+----------+-----------+
| 0| 5.1| 3.5| 1.4| 0.2|Iris-setosa|
| 1| 4.9| 3| 1.4| 0.2|Iris-setosa|
| 2| 4.7| 3.2| 1.3| 0.2|Iris-setosa|
| 3| 4.6| 3.1| 1.5| 0.2|Iris-setosa|
| 4| 5| 3.6| 1.4| 0.2|Iris-setosa|
| 5| 5.4| 3.9| 1.7| 0.4|Iris-setosa|
| 6| 4.6| 3.4| 1.4| 0.3|Iris-setosa|
| 7| 5| 3.4| 1.5| 0.2|Iris-setosa|
| 8| 4.4| 2.9| 1.4| 0.2|Iris-setosa|
| 9| 4.9| 3.1| 1.5| 0.1|Iris-setosa|
+---+-----------+----------+-----------+----------+-----------+
140 more rows...
The smile.datasets package also provides many public datasets as
ready-made objects (no file path required).
var iris = new Iris().data() // use built-in dataset object
+---+-----------+----------+-----------+----------+-----------+
| |sepallength|sepalwidth|petallength|petalwidth| class|
+---+-----------+----------+-----------+----------+-----------+
| 0| 5.1| 3.5| 1.4| 0.2|Iris-setosa|
| 1| 4.9| 3| 1.4| 0.2|Iris-setosa|
| 2| 4.7| 3.2| 1.3| 0.2|Iris-setosa|
| 3| 4.6| 3.1| 1.5| 0.2|Iris-setosa|
| 4| 5| 3.6| 1.4| 0.2|Iris-setosa|
| 5| 5.4| 3.9| 1.7| 0.4|Iris-setosa|
| 6| 4.6| 3.4| 1.4| 0.3|Iris-setosa|
| 7| 5| 3.4| 1.5| 0.2|Iris-setosa|
| 8| 4.4| 2.9| 1.4| 0.2|Iris-setosa|
| 9| 4.9| 3.1| 1.5| 0.1|Iris-setosa|
+---+-----------+----------+-----------+----------+-----------+
140 more rows...
Indexing
We can set a row index to the data frames that must be of length the number
of rows with no duplicates nor missing values. The row index serves as a
unique identifier for each row in a DataFrame. It allows you to access
rows efficiently with the loc() method. Compared with the normal
ordinal index, the object-based row index often contains certain semantic
meaning.
var df = DataFrame.of(MathEx.randn(6, 4))
var dates = Dates.range(LocalDate.of(2025,2,1), 6)
df = df.setIndex(dates)
df.loc(dates[1])
df.loc(dates[1], dates[2])
+----------+---------+---------+---------+---------+
| | V1| V2| V3| V4|
+----------+---------+---------+---------+---------+
|2025-02-01|-0.201469| 0.970363| 2.726932|-0.146014|
|2025-02-02| 1.872161| 0.495932| 0.553859|-0.028237|
|2025-02-03|-0.504866|-0.179409| 0.201377| 0.281267|
|2025-02-04| 0.894446| 0.791521| 0.053346| 0.213519|
|2025-02-05| 0.200011|-0.203736|-0.349196|-1.193759|
|2025-02-06| 1.52529|-1.407597| 1.16758| -1.78291|
+----------+---------+---------+---------+---------+
{
V1: -0.133296,
V2: -2.461161,
V3: 0.25011,
V4: 1.132062
}
+----------+---------+---------+---------+--------+
| | V1| V2| V3| V4|
+----------+---------+---------+---------+--------+
|2025-02-02|-0.133296|-2.461161| 0.25011|1.132062|
|2025-02-03| 0.25248|-0.063054|-1.128157| 0.37634|
+----------+---------+---------+---------+--------+
The row index may be an existing column, which will be removed in the resulting data frame.
var df = new DataFrame(
ValueVector.of("A", 1.0),
ValueVector.of("B", LocalDate.parse("2013-01-02")),
ValueVector.of("C", "foo"),
ObjectVector.of("D", Index.range(0, 4).toArray()),
ObjectVector.of("E", new int[]{3, 3, 3, 3})
)
df = df.setIndex("B")
+----------+---+---+------------+------------+
| | A| C| D| E|
+----------+---+---+------------+------------+
|2013-01-02| 1|foo|[0, 1, 2, 3]|[3, 3, 3, 3]|
+----------+---+---+------------+------------+
Viewing Data
By default, DataFrame.toString() returns a pretty print of
top 10 rows. You may also use DataFrame.head() and
DataFrame.tail() with a specified number to view the top
and bottom rows of the frame respectively.
println(df.head(3))
println(df.tail(3))
+----------+---------+---------+---------+---------+
| | V1| V2| V3| V4|
+----------+---------+---------+---------+---------+
|2025-02-01|-0.189794| 0.609897|-0.289189|-0.636956|
|2025-02-02|-0.133296|-2.461161| 0.25011| 1.132062|
|2025-02-03| 0.25248|-0.063054|-1.128157| 0.37634|
+----------+---------+---------+---------+---------+
3 more rows...
+----------+---------+---------+---------+--------+
| | V1| V2| V3| V4|
+----------+---------+---------+---------+--------+
|2025-02-04|-1.391481| 1.398828| 0.294973|1.353308|
|2025-02-05| 2.812277| 0.82762|-0.294806|1.836631|
|2025-02-06| 1.091213|-0.190432| 1.963064|0.725228|
+----------+---------+---------+---------+--------+
The method describe() shows the data structure and statistic summary:
iris.describe()
+---+-----------+-----+--------------------+-----+--------+--------+---+---+---+---+---+
| | column| type| measure|count| mean| std|min|25%|50%|75%|max|
+---+-----------+-----+--------------------+-----+--------+--------+---+---+---+---+---+
| 0|sepallength|float| null| 150|5.843333|0.828066|4.3|5.1|5.8|6.4|7.9|
| 1| sepalwidth|float| null| 150| 3.054|0.433594| 2|2.8| 3|3.3|4.4|
| 2|petallength|float| null| 150|3.758667| 1.76442| 1|1.6|4.4|5.1|6.9|
| 3| petalwidth|float| null| 150|1.198667|0.763161|0.1|0.3|1.3|1.8|2.5|
| 4| class| byte|nominal[Iris-seto...| 150| NaN| NaN| 0| 0| 1| 2| 2|
+---+-----------+-----+--------------------+-----+--------+--------+---+---+---+---+---+
Selection
We can get a row with the array syntax or slice a subset of rows.
iris.get(0)
iris.get(Index.range(10, 20))
{
sepallength: 5.1,
sepalwidth: 3.5,
petallength: 1.4,
petalwidth: 0.2,
class: Iris-setosa
}
+---+-----------+----------+-----------+----------+-----------+
| |sepallength|sepalwidth|petallength|petalwidth| class|
+---+-----------+----------+-----------+----------+-----------+
| 0| 5.4| 3.7| 1.5| 0.2|Iris-setosa|
| 1| 4.8| 3.4| 1.6| 0.2|Iris-setosa|
| 2| 4.8| 3| 1.4| 0.1|Iris-setosa|
| 3| 4.3| 3| 1.1| 0.1|Iris-setosa|
| 4| 5.8| 4| 1.2| 0.2|Iris-setosa|
| 5| 5.7| 4.4| 1.5| 0.4|Iris-setosa|
| 6| 5.4| 3.9| 1.3| 0.4|Iris-setosa|
| 7| 5.1| 3.5| 1.4| 0.3|Iris-setosa|
| 8| 5.7| 3.8| 1.7| 0.3|Iris-setosa|
| 9| 5.1| 3.8| 1.5| 0.3|Iris-setosa|
+---+-----------+----------+-----------+----------+-----------+
iris(0)
iris(Index.range(10, 20))
{
sepallength: 5.1,
sepalwidth: 3.5,
petallength: 1.4,
petalwidth: 0.2,
class: Iris-setosa
}
+---+-----------+----------+-----------+----------+-----------+
| |sepallength|sepalwidth|petallength|petalwidth| class|
+---+-----------+----------+-----------+----------+-----------+
| 0| 5.4| 3.7| 1.5| 0.2|Iris-setosa|
| 1| 4.8| 3.4| 1.6| 0.2|Iris-setosa|
| 2| 4.8| 3| 1.4| 0.1|Iris-setosa|
| 3| 4.3| 3| 1.1| 0.1|Iris-setosa|
| 4| 5.8| 4| 1.2| 0.2|Iris-setosa|
| 5| 5.7| 4.4| 1.5| 0.4|Iris-setosa|
| 6| 5.4| 3.9| 1.3| 0.4|Iris-setosa|
| 7| 5.1| 3.5| 1.4| 0.3|Iris-setosa|
| 8| 5.7| 3.8| 1.7| 0.3|Iris-setosa|
| 9| 5.1| 3.8| 1.5| 0.3|Iris-setosa|
+---+-----------+----------+-----------+----------+-----------+
iris[0]
iris.get(Index.range(10, 20))
{
sepallength: 5.1,
sepalwidth: 3.5,
petallength: 1.4,
petalwidth: 0.2,
class: Iris-setosa
}
+---+-----------+----------+-----------+----------+-----------+
| |sepallength|sepalwidth|petallength|petalwidth| class|
+---+-----------+----------+-----------+----------+-----------+
| 0| 5.4| 3.7| 1.5| 0.2|Iris-setosa|
| 1| 4.8| 3.4| 1.6| 0.2|Iris-setosa|
| 2| 4.8| 3| 1.4| 0.1|Iris-setosa|
| 3| 4.3| 3| 1.1| 0.1|Iris-setosa|
| 4| 5.8| 4| 1.2| 0.2|Iris-setosa|
| 5| 5.7| 4.4| 1.5| 0.4|Iris-setosa|
| 6| 5.4| 3.9| 1.3| 0.4|Iris-setosa|
| 7| 5.1| 3.5| 1.4| 0.3|Iris-setosa|
| 8| 5.7| 3.8| 1.7| 0.3|Iris-setosa|
| 9| 5.1| 3.8| 1.5| 0.3|Iris-setosa|
+---+-----------+----------+-----------+----------+-----------+
Similarly, we can refer a column by its name or select a few columns to create a new data frame.
iris.column("sepallength")
iris.select("sepallength", "sepalwidth")
sepallength[5.1, 4.9, 4.7, 4.6, 5, 5.4, 4.6, 5, 4.4, 4.9, ..., 140 more]
+---+-----------+----------+
| |sepallength|sepalwidth|
+---+-----------+----------+
| 0| 5.1| 3.5|
| 1| 4.9| 3|
| 2| 4.7| 3.2|
| 3| 4.6| 3.1|
| 4| 5| 3.6|
| 5| 5.4| 3.9|
| 6| 4.6| 3.4|
| 7| 5| 3.4|
| 8| 4.4| 2.9|
| 9| 4.9| 3.1|
+---+-----------+----------+
140 more rows...
iris("sepallength")
iris("sepallength", "sepalwidth")
sepallength[5.1, 4.9, 4.7, 4.6, 5, 5.4, 4.6, 5, 4.4, 4.9, ..., 140 more]
+---+-----------+----------+
| |sepallength|sepalwidth|
+---+-----------+----------+
| 0| 5.1| 3.5|
| 1| 4.9| 3|
| 2| 4.7| 3.2|
| 3| 4.6| 3.1|
| 4| 5| 3.6|
| 5| 5.4| 3.9|
| 6| 4.6| 3.4|
| 7| 5| 3.4|
| 8| 4.4| 2.9|
| 9| 4.9| 3.1|
+---+-----------+----------+
140 more rows...
iris.column("sepallength")
iris.select("sepallength", "sepalwidth")
sepallength[5.1, 4.9, 4.7, 4.6, 5, 5.4, 4.6, 5, 4.4, 4.9, ..., 140 more]
+---+-----------+----------+
| |sepallength|sepalwidth|
+---+-----------+----------+
| 0| 5.1| 3.5|
| 1| 4.9| 3|
| 2| 4.7| 3.2|
| 3| 4.6| 3.1|
| 4| 5| 3.6|
| 5| 5.4| 3.9|
| 6| 4.6| 3.4|
| 7| 5| 3.4|
| 8| 4.4| 2.9|
| 9| 4.9| 3.1|
+---+-----------+----------+
140 more rows...
We can also select rows with boolean indexing. The below example uses
isin() method for filtering:
iris.get(iris.column("class").isin("Iris-setosa", "Iris-virginica"))
+---+-----------+----------+-----------+----------+-----------+
| |sepallength|sepalwidth|petallength|petalwidth| class|
+---+-----------+----------+-----------+----------+-----------+
| 0| 5.1| 3.5| 1.4| 0.2|Iris-setosa|
| 1| 4.9| 3| 1.4| 0.2|Iris-setosa|
| 2| 4.7| 3.2| 1.3| 0.2|Iris-setosa|
| 3| 4.6| 3.1| 1.5| 0.2|Iris-setosa|
| 4| 5| 3.6| 1.4| 0.2|Iris-setosa|
| 5| 5.4| 3.9| 1.7| 0.4|Iris-setosa|
| 6| 4.6| 3.4| 1.4| 0.3|Iris-setosa|
| 7| 5| 3.4| 1.5| 0.2|Iris-setosa|
| 8| 4.4| 2.9| 1.4| 0.2|Iris-setosa|
| 9| 4.9| 3.1| 1.5| 0.1|Iris-setosa|
+---+-----------+----------+-----------+----------+-----------+
90 more rows...
Setting
Setting values by position:
iris.set(0, 0, 1.5)
Adding columns:
var df = DataFrame.of(MathEx.randn(150, 3))
iris.add(df.column("V1"), df.column("V3"))
Setting a column:
iris.set("V1", df.column("V3"))
Merge
The merge() method can combine data frames horizontally:
var df3 = iris.merge(df)
To concatenate data frames vertically, use concat():
var iris2 = iris.concat(iris)
To join two data frames on their index:
var dates = Dates.range(LocalDate.of(2025,2,1), 6);
var df1 = DataFrame.of(MathEx.randn(6, 4)).setIndex(dates);
var df2 = DataFrame.of(MathEx.randn(6, 4)).setIndex(dates);
var df = df1.join(df2);
Missing Data
For object column, null indicates missing data. For primitive
columns, SMILE maintains a bit mask to indicate missing values. However,
it is conventional that users use NaN to represent missing data
for floating numeric types. One may use DataFrame.isNullAt(i, j)
to check if a cell is null or NaN.
DataFrame.dropna() drops any rows that have null or missing value:
var df = DataFrame.of(MathEx.randn(6, 4))
df.set(0, 0, Double.NaN)
df.set(1, 3, Double.NaN)
df.dropna()
+---+---------+---------+---------+---------+
| | V1| V2| V3| V4|
+---+---------+---------+---------+---------+
| 0|-0.504866|-0.179409| 0.201377| 0.281267|
| 1| 0.894446| 0.791521| 0.053346| 0.213519|
| 2| 0.200011|-0.203736|-0.349196|-1.193759|
| 3| 1.52529|-1.407597| 1.16758| -1.78291|
+---+---------+---------+---------+---------+
DataFrame.fillna() fills missing data in numeric columns:
df.fillna(100)
+---+---------+---------+---------+---------+
| | V1| V2| V3| V4|
+---+---------+---------+---------+---------+
| 0| 100| 0.970363| 2.726932|-0.146014|
| 1| 1.872161| 0.495932| 0.553859| 100|
| 2|-0.504866|-0.179409| 0.201377| 0.281267|
| 3| 0.894446| 0.791521| 0.053346| 0.213519|
| 4| 0.200011|-0.203736|-0.349196|-1.193759|
| 5| 1.52529|-1.407597| 1.16758| -1.78291|
+---+---------+---------+---------+---------+
Operations
Advanced operations such as exists, forall,
find, filter are also supported. In Java API,
all these operations are on Stream. The corresponding methods
are anyMatch, allMatch, findAny,
and filter.
The predicate of these functions expect a Tuple
iris.stream().anyMatch(row -> row.getDouble(0) > 4.5)
iris.stream().allMatch(row -> row.getDouble(0) < 10)
iris.stream().filter(row -> row.getByte("class") == 1).findAny()
iris.stream().filter(row -> row.getString("class").equals("Iris-versicolor")).findAny()
var stream = iris.stream().filter(row -> row.getDouble(1) > 3 && row.getByte("class") != 0)
DataFrame.of(iris.schema(), stream)
true
true
Optional[{
sepallength: 6.2,
sepalwidth: 2.9,
petallength: 4.3,
petalwidth: 1.3,
class: Iris-versicolor
}]
Optional[{
sepallength: 6.2,
sepalwidth: 2.9,
petallength: 4.3,
petalwidth: 1.3,
class: Iris-versicolor
}]
+-----------+----------+-----------+----------+---------------+
|sepallength|sepalwidth|petallength|petalwidth| class|
+-----------+----------+-----------+----------+---------------+
| 7| 3.2| 4.7| 1.4|Iris-versicolor|
| 6.4| 3.2| 4.5| 1.5|Iris-versicolor|
| 6.9| 3.1| 4.9| 1.5|Iris-versicolor|
| 6.3| 3.3| 4.7| 1.6|Iris-versicolor|
| 6.7| 3.1| 4.4| 1.4|Iris-versicolor|
| 6| 3.4| 4.5| 1.6|Iris-versicolor|
| 6.7| 3.1| 4.7| 1.5|Iris-versicolor|
| 6.3| 3.3| 6| 2.5| Iris-virginica|
| 7.2| 3.6| 6.1| 2.5| Iris-virginica|
+-----------+----------+-----------+----------+---------------+
15 more rows...
iris.exists(_.getDouble(0) > 4.5)
iris.forall(_.getDouble(0) < 10)
iris.find(_("class") == 1)
iris.find(_.getString("class").equals("Iris-versicolor"))
iris.filter { row => row.getDouble(1) > 3 && row("class") != 0 }
true
true
Optional[{
sepallength: 6.2,
sepalwidth: 2.9,
petallength: 4.3,
petalwidth: 1.3,
class: Iris-versicolor
}]
Optional[{
sepallength: 6.2,
sepalwidth: 2.9,
petallength: 4.3,
petalwidth: 1.3,
class: Iris-versicolor
}]
+-----------+----------+-----------+----------+---------------+
|sepallength|sepalwidth|petallength|petalwidth| class|
+-----------+----------+-----------+----------+---------------+
| 7| 3.2| 4.7| 1.4|Iris-versicolor|
| 6.4| 3.2| 4.5| 1.5|Iris-versicolor|
| 6.9| 3.1| 4.9| 1.5|Iris-versicolor|
| 6.3| 3.3| 4.7| 1.6|Iris-versicolor|
| 6.7| 3.1| 4.4| 1.4|Iris-versicolor|
| 5.9| 3.2| 4.8| 1.8|Iris-versicolor|
| 6| 3.4| 4.5| 1.6|Iris-versicolor|
| 6.7| 3.1| 4.7| 1.5|Iris-versicolor|
| 6.3| 3.3| 6| 2.5| Iris-virginica|
| 7.2| 3.6| 6.1| 2.5| Iris-virginica|
+-----------+----------+-----------+----------+---------------+
15 more rows...
iris.stream().anyMatch({row -> row.getDouble(0) > 4.5})
iris.stream().allMatch({row -> row.getDouble(0) < 10})
iris.stream().filter({row -> row.getByte("class") == 1.toByte()}).findAny()
iris.stream().filter({row -> row.getString("class").equals("Iris-versicolor")}).findAny()
DataFrame.of(iris.stream().filter({row -> row.getDouble(1) > 3 && row.getByte("class") != 0.toByte()}))
true
true
Optional[{
sepallength: 6.2,
sepalwidth: 2.9,
petallength: 4.3,
petalwidth: 1.3,
class: Iris-versicolor
}]
Optional[{
sepallength: 5.4,
sepalwidth: 3,
petallength: 4.5,
petalwidth: 1.5,
class: Iris-versicolor
}]
+-----------+----------+-----------+----------+---------------+
|sepallength|sepalwidth|petallength|petalwidth| class|
+-----------+----------+-----------+----------+---------------+
| 7| 3.2| 4.7| 1.4|Iris-versicolor|
| 6.4| 3.2| 4.5| 1.5|Iris-versicolor|
| 6.9| 3.1| 4.9| 1.5|Iris-versicolor|
| 6.3| 3.3| 4.7| 1.6|Iris-versicolor|
| 6.7| 3.1| 4.4| 1.4|Iris-versicolor|
| 5.9| 3.2| 4.8| 1.8|Iris-versicolor|
| 6| 3.4| 4.5| 1.6|Iris-versicolor|
| 6.7| 3.1| 4.7| 1.5|Iris-versicolor|
| 6.3| 3.3| 6| 2.5| Iris-virginica|
| 7.2| 3.6| 6.1| 2.5| Iris-virginica|
+-----------+----------+-----------+----------+---------------+
15 more rows...
For data wrangling, the most important functions of DataFrame
are map and groupBy.
var x6 = iris.stream().map(row -> {
var x = new double[6];
for (int i = 0; i < 4; i++) x[i] = row.getDouble(i);
x[4] = x[0] * x[1];
x[5] = x[2] * x[3];
return x;
})
x6.forEach(xi -> System.out.println(Arrays.toString(xi)))
[6.199999809265137, 2.9000000953674316, 4.300000190734863, 1.2999999523162842, 17.980000038146954, 5.590000042915335]
[7.300000190734863, 2.9000000953674316, 6.300000190734863, 1.7999999523162842, 21.170001249313373, 11.340000042915335]
[7.699999809265137, 3.0, 6.099999904632568, 2.299999952316284, 23.09999942779541, 14.029999489784245]
[6.699999809265137, 2.5, 5.800000190734863, 1.7999999523162842, 16.749999523162842, 10.440000066757193]
[7.199999809265137, 3.5999999046325684, 6.099999904632568, 2.5, 25.919998626709003, 15.249999761581421]
[6.5, 3.200000047683716, 5.099999904632568, 2.0, 20.800000309944153, 10.199999809265137]
[6.400000095367432, 2.700000047683716, 5.300000190734863, 1.899999976158142, 17.28000056266785, 10.070000236034389]
[5.699999809265137, 2.5999999046325684, 3.5, 1.0, 14.819998960495013, 3.5]
[4.599999904632568, 3.5999999046325684, 1.0, 0.20000000298023224, 16.55999921798707, 0.20000000298023224]
[5.400000095367432, 3.0, 4.5, 1.5, 16.200000286102295, 6.75]
[6.699999809265137, 3.0999999046325684, 4.400000095367432, 1.399999976158142, 20.76999876976015, 6.160000028610227]
[5.099999904632568, 3.799999952316284, 1.600000023841858, 0.20000000298023224, 19.379999394416814, 0.32000000953674324]
[5.599999904632568, 3.0, 4.5, 1.5, 16.799999713897705, 6.75]
[6.0, 3.4000000953674316, 4.5, 1.600000023841858, 20.40000057220459, 7.200000107288361]
[5.099999904632568, 3.299999952316284, 1.7000000476837158, 0.5, 16.82999944210053, 0.8500000238418579]
[5.5, 2.4000000953674316, 3.799999952316284, 1.100000023841858, 13.200000524520874, 4.1800000381469715]
[7.099999904632568, 3.0, 5.900000095367432, 2.0999999046325684, 21.299999713897705, 12.38999963760375]
[6.300000190734863, 3.4000000953674316, 5.599999904632568, 2.4000000953674316, 21.420001249313373, 13.440000305175772]
[5.099999904632568, 2.5, 3.0, 1.100000023841858, 12.749999761581421, 3.3000000715255737]
[6.400000095367432, 3.0999999046325684, 5.5, 1.7999999523162842, 19.839999685287466, 9.899999737739563]
[6.300000190734863, 2.9000000953674316, 5.599999904632568, 1.7999999523162842, 18.27000115394594, 10.079999561309819]
[5.5, 2.4000000953674316, 3.700000047683716, 1.0, 13.200000524520874, 3.700000047683716]
[6.5, 3.0, 5.800000190734863, 2.200000047683716, 19.5, 12.76000069618226]
[7.599999904632568, 3.0, 6.599999904632568, 2.0999999046325684, 22.799999713897705, 13.859999170303354]
[4.900000095367432, 2.5, 4.5, 1.7000000476837158, 12.250000238418579, 7.650000214576721]
[5.0, 2.299999952316284, 3.299999952316284, 1.0, 11.499999761581421, 3.299999952316284]
[5.599999904632568, 2.700000047683716, 4.199999809265137, 1.2999999523162842, 15.120000009536739, 5.45999955177308]
...
java.util.stream.ReferencePipeline$3@32eff876
iris.map { row =>
val x = new Array[Double](6)
for (i <- 0 until 4) x(i) = row.getDouble(i)
x(4) = x(0) * x(1)
x(5) = x(2) * x(3)
}
ArrayBuffer(
Array(5.1, 3.5, 1.4, 0.2, 17.849999999999998, 0.27999999999999997),
Array(4.9, 3.0, 1.4, 0.2, 14.700000000000001, 0.27999999999999997),
Array(4.7, 3.2, 1.3, 0.2, 15.040000000000001, 0.26),
Array(4.6, 3.1, 1.5, 0.2, 14.26, 0.30000000000000004),
Array(5.0, 3.6, 1.4, 0.2, 18.0, 0.27999999999999997),
Array(5.4, 3.9, 1.7, 0.4, 21.060000000000002, 0.68),
Array(4.6, 3.4, 1.4, 0.3, 15.639999999999999, 0.42),
Array(5.0, 3.4, 1.5, 0.2, 17.0, 0.30000000000000004),
Array(4.4, 2.9, 1.4, 0.2, 12.76, 0.27999999999999997),
Array(4.9, 3.1, 1.5, 0.1, 15.190000000000001, 0.15000000000000002),
Array(5.4, 3.7, 1.5, 0.2, 19.980000000000004, 0.30000000000000004),
Array(4.8, 3.4, 1.6, 0.2, 16.32, 0.32000000000000006),
Array(4.8, 3.0, 1.4, 0.1, 14.399999999999999, 0.13999999999999999),
Array(4.3, 3.0, 1.1, 0.1, 12.899999999999999, 0.11000000000000001),
Array(5.8, 4.0, 1.2, 0.2, 23.2, 0.24),
Array(5.7, 4.4, 1.5, 0.4, 25.080000000000002, 0.6000000000000001),
Array(5.4, 3.9, 1.3, 0.4, 21.060000000000002, 0.52),
Array(5.1, 3.5, 1.4, 0.3, 17.849999999999998, 0.42),
Array(5.7, 3.8, 1.7, 0.3, 21.66, 0.51),
Array(5.1, 3.8, 1.5, 0.3, 19.38, 0.44999999999999996),
Array(5.4, 3.4, 1.7, 0.2, 18.36, 0.34),
Array(5.1, 3.7, 1.5, 0.4, 18.87, 0.6000000000000001),
Array(4.6, 3.6, 1.0, 0.2, 16.56, 0.2),
Array(5.1, 3.3, 1.7, 0.5, 16.83, 0.85),
...
val x6 = iris.stream().map({row ->
... val x = DoubleArray(6)
... for (i in 0..3) x[i] = row.getDouble(i)
... x[4] = x[0] * x[1]
... x[5] = x[2] * x[3]
... x
... })
x6.forEach({xi: DoubleArray -> println(java.util.Arrays.toString(xi))})
[5.699999809265137, 2.5999999046325684, 3.5, 1.0, 14.819998960495013, 3.5]
[6.699999809265137, 3.0999999046325684, 4.400000095367432, 1.399999976158142, 20.76999876976015, 6.160000028610227]
[5.400000095367432, 3.0, 4.5, 1.5, 16.200000286102295, 6.75]
[5.5, 2.4000000953674316, 3.799999952316284, 1.100000023841858, 13.200000524520874, 4.1800000381469715]
[5.599999904632568, 3.0, 4.5, 1.5, 16.799999713897705, 6.75]
[4.900000095367432, 3.0999999046325684, 1.5, 0.10000000149011612, 15.189999828338614, 0.15000000223517418]
[4.599999904632568, 3.5999999046325684, 1.0, 0.20000000298023224, 16.55999921798707, 0.20000000298023224]
[7.699999809265137, 3.0, 6.099999904632568, 2.299999952316284, 23.09999942779541, 14.029999489784245]
[5.400000095367432, 3.700000047683716, 1.5, 0.20000000298023224, 19.980000610351567, 0.30000000447034836]
[5.800000190734863, 2.700000047683716, 4.099999904632568, 1.0, 15.660000791549692, 4.099999904632568]
[6.300000190734863, 3.4000000953674316, 5.599999904632568, 2.4000000953674316, 21.420001249313373, 13.440000305175772]
[6.0, 3.4000000953674316, 4.5, 1.600000023841858, 20.40000057220459, 7.200000107288361]
[6.199999809265137, 2.200000047683716, 4.5, 1.5, 13.63999987602233, 6.75]
[6.400000095367432, 3.0999999046325684, 5.5, 1.7999999523162842, 19.839999685287466, 9.899999737739563]
[6.699999809265137, 3.0999999046325684, 4.699999809265137, 1.5, 20.76999876976015, 7.049999713897705]
[5.5, 2.4000000953674316, 3.700000047683716, 1.0, 13.200000524520874, 3.700000047683716]
[5.099999904632568, 3.799999952316284, 1.600000023841858, 0.20000000298023224, 19.379999394416814, 0.32000000953674324]
[6.199999809265137, 2.9000000953674316, 4.300000190734863, 1.2999999523162842, 17.980000038146954, 5.590000042915335]
[6.300000190734863, 2.299999952316284, 4.400000095367432, 1.2999999523162842, 14.490000138282767, 5.719999914169307]
[5.800000190734863, 2.700000047683716, 3.9000000953674316, 1.2000000476837158, 15.660000791549692, 4.680000300407414]
[6.0, 3.0, 4.800000190734863, 1.7999999523162842, 18.0, 8.640000114440909]
[5.599999904632568, 2.5, 3.9000000953674316, 1.100000023841858, 13.999999761581421, 4.290000197887423]
[4.800000190734863, 3.4000000953674316, 1.600000023841858, 0.20000000298023224, 16.320001106262225, 0.32000000953674324]
[6.900000095367432, 3.0999999046325684, 5.400000095367432, 2.0999999046325684, 21.38999963760375, 11.339999685287466]
[5.900000095367432, 3.200000047683716, 4.800000190734863, 1.7999999523162842, 18.88000058650971, 8.640000114440909]
[4.800000190734863, 3.0, 1.399999976158142, 0.10000000149011612, 14.40000057220459, 0.13999999970197674]
[5.099999904632568, 3.299999952316284, 1.7000000476837158, 0.5, 16.82999944210053, 0.8500000238418579]
[6.099999904632568, 2.799999952316284, 4.0, 1.2999999523162842, 17.07999944210053, 5.199999809265137]
[7.900000095367432, 3.799999952316284, 6.400000095367432, 2.0, 30.01999998569488, 12.800000190734863]
[6.0, 2.700000047683716, 5.099999904632568, 1.600000023841858, 16.200000286102295, 8.159999969005582]
[6.400000095367432, 2.799999952316284, 5.599999904632568, 2.200000047683716, 17.919999961853023, 12.320000057220454]
[6.599999904632568, 3.0, 4.400000095367432, 1.399999976158142, 19.799999713897705, 6.160000028610227]
...
The groupBy operation groups elements according to a classification
function, and returning the results in a Map. The classification
function maps elements to some key type K. The collector produces
a map whose keys are the values resulting from applying the classification
function to the input elements, and whose corresponding values are Lists
containing the input elements which map to the associated key under the
classification function.
iris.stream().collect(java.util.stream.Collectors.groupingBy(row -> row.getString("class")))
{Iris-versicolor=[{
sepallength: 7,
sepalwidth: 3.2,
petallength: 4.7,
petalwidth: 1.4,
class: Iris-versicolor
}, {
sepallength: 6.4,
sepalwidth: 3.2,
petallength: 4.5,
petalwidth: 1.5,
class: Iris-versicolor
}, {
sepallength: 6.9,
sepalwidth: 3.1,
petallength: 4.9,
petalwidth: 1.5,
class: Iris-versicolor
}, {
sepallength: 5.5,
sepalwidth: 2.3,
petallength: 4,
petalwidth: 1.3,
class: Iris-versicolor
}, {
sepallength: 6.5,
sepalwidth: 2.8,
petallength: 4.6,
petalwidth: 1.5,
class: Iris-versicolor
}, {
sepallength: 5.7,
sepalwidth: 2.8,
petallength: 4.5,
petalwidth: 1.3,
class: Iris-versicolor
}, ... class: Iris-setosa
}, {
sepallength: 4.6,
sepalwidth: 3.2,
petallength: 1.4,
petalwidth: 0.2,
class: Iris-setosa
}, {
sepallength: 5.3,
sepalwidth: 3.7,
petallength: 1.5,
petalwidth: 0.2,
class: Iris-setosa
}, {
sepallength: 5,
sepalwidth: 3.3,
petallength: 1.4,
petalwidth: 0.2,
class: Iris-setosa
}]}
iris.groupBy(row => row.getString("class"))
Map(
"Iris-virginica" ->
+-----------+----------+-----------+----------+--------------+
|sepallength|sepalwidth|petallength|petalwidth| class|
+-----------+----------+-----------+----------+--------------+
| 6.3| 3.3| 6| 2.5|Iris-virginica|
| 5.8| 2.7| 5.1| 1.9|Iris-virginica|
| 7.1| 3| 5.9| 2.1|Iris-virginica|
| 6.3| 2.9| 5.6| 1.8|Iris-virginica|
| 6.5| 3| 5.8| 2.2|Iris-virginica|
| 7.6| 3| 6.6| 2.1|Iris-virginica|
| 4.9| 2.5| 4.5| 1.7|Iris-virginica|
| 7.3| 2.9| 6.3| 1.8|Iris-virginica|
| 6.7| 2.5| 5.8| 1.8|Iris-virginica|
| 7.2| 3.6| 6.1| 2.5|Iris-virginica|
+-----------+----------+-----------+----------+--------------+
40 more rows...
,
"Iris-versicolor" ->
+-----------+----------+-----------+----------+---------------+
|sepallength|sepalwidth|petallength|petalwidth| class|
+-----------+----------+-----------+----------+---------------+
| 7| 3.2| 4.7| 1.4|Iris-versicolor|
| 6.4| 3.2| 4.5| 1.5|Iris-versicolor|
| 6.9| 3.1| 4.9| 1.5|Iris-versicolor|
...
iris.stream().collect(java.util.stream.Collectors.groupingBy({row: Tuple -> row.getString("class")}))
{Iris-versicolor=[{
sepallength: 7,
sepalwidth: 3.2,
petallength: 4.7,
petalwidth: 1.4,
class: Iris-versicolor
}, {
sepallength: 6.4,
sepalwidth: 3.2,
petallength: 4.5,
petalwidth: 1.5,
class: Iris-versicolor
}, {
sepallength: 6.9,
sepalwidth: 3.1,
petallength: 4.9,
petalwidth: 1.5,
class: Iris-versicolor
}, {
sepallength: 5.5,
sepalwidth: 2.3,
petallength: 4,
petalwidth: 1.3,
class: Iris-versicolor
}, {
sepallength: 6.5,
sepalwidth: 2.8,
petallength: 4.6,
petalwidth: 1.5,
class: Iris-versicolor
}, {
sepallength: 5.7,
sepalwidth: 2.8,
petallength: 4.5,
petalwidth: 1.3,
class: Iris-versicolor
}, {
sepallength: 6.3,
sepalwidth: 3.3,
petallength: 4.7,
petalwidth: 1.6,
class: Iris-versicolor
}, {
sepallength: 4.9,
sepalwidth: 2.4,
petallength: 3.3,
petalwidth: 1,
class: Iris-versicolor
}, {
...
SQL
While SMILE provides many imperative ways to manipulate DataFrames as shown above, it is often easier to do so with SQL.
SQL sql = new SQL();
sql.parquet("user", "data/kylo/userdata1.parquet");
sql.json("books", "data/kylo/books_array.json");
sql.csv("gdp", "data/regression/gdp.csv");
sql.csv("diabetes", "data/regression/diabetes.csv");
var tables = sql.tables();
var columns = sql.describe("user");
+----------+-------+
|TABLE_NAME|REMARKS|
+----------+-------+
| books| null|
| diabetes| null|
| gdp| null|
| user| null|
+----------+-------+
+-----------------+---------+-----------+
| COLUMN_NAME|TYPE_NAME|IS_NULLABLE|
+-----------------+---------+-----------+
|registration_dttm|TIMESTAMP| YES|
| id| INTEGER| YES|
| first_name| VARCHAR| YES|
| last_name| VARCHAR| YES|
| email| VARCHAR| YES|
| gender| VARCHAR| YES|
| ip_address| VARCHAR| YES|
| cc| VARCHAR| YES|
| country| VARCHAR| YES|
| birthdate| VARCHAR| YES|
+-----------------+---------+-----------+
3 more rows...
In the above, we create a database and create four tables by loading parquet, json, and csv files.
We also use the describe function to obtain the schema of the table user. With SQL,
it is easy to filter data and the result is a DataFrame.
var user = sql.query("SELECT * FROM user WHERE country = 'China'");
+-------------------+---+----------+----------+--------------------+------+---------------+-------------------+-------+---------+---------+--------------------+--------+
| registration_dttm| id|first_name| last_name| email|gender| ip_address| cc|country|birthdate| salary| title|comments|
+-------------------+---+----------+----------+--------------------+------+---------------+-------------------+-------+---------+---------+--------------------+--------+
|2016-02-03T00:36:21| 4| Denise| Riley| driley3@gmpg.org|Female| 140.35.109.83| 3576031598965625| China| 4/8/1997| 90263.05|Senior Cost Accou...| |
|2016-02-03T18:04:34| 12| Alice| Berry|aberryb@wikipedia...|Female| 246.225.12.189| 4917830851454417| China|8/12/1968| 22944.53| Quality Engineer| |
|2016-02-03T10:30:36| 20| Rebecca| Bell| rbellj@bandcamp.com|Female|172.215.104.127| | China| |137251.19| | |
|2016-02-03T08:41:26| 27| Henry| Henry| hhenryq@godaddy.com| Male| 191.88.236.116|4905730021217853521| China|9/22/1995|284300.15|Nuclear Power Eng...| |
|2016-02-03T20:46:39| 37| Dorothy| Gomez|dgomez10@jiathis.com|Female| 65.111.200.146| 493684876859391834| China| | 57194.86| | |
|2016-02-03T08:34:26| 43| Amanda| Gray| agray16@cdbaby.com|Female| 252.20.193.145| 3561501596653859| China|8/28/1967|213410.26|Senior Quality En...| |
|2016-02-03T00:05:52| 53| Ralph| Price| rprice1g@tmall.com| Male| 152.6.235.33| 4844227560658222| China|8/26/1986| 168208.4| Teacher| |
|2016-02-03T16:03:13| 55| Anna|Montgomery|amontgomery1i@goo...|Female| 80.111.141.47| 3586860392406446| China| 9/6/1957| 92837.5|Software Test Eng...| 1E2|
|2016-02-03T00:33:25| 57| Willie| Palmer|wpalmer1k@t-onlin...| Male| 164.107.46.161| 4026614769857244| China|8/23/1986|184978.64|Environmental Spe...| |
|2016-02-03T05:55:57| 58| Arthur| Berry| aberry1l@unc.edu| Male| 52.42.24.55| 3542761473624274| China| |144164.88| | |
+-------------------+---+----------+----------+--------------------+------+---------------+-------------------+-------+---------+---------+--------------------+--------+
179 more rows...
Of course, join is very useful to prepare data from multiple sources. The result DataFrame may be feed to downstream machine learning algorithms.
var gdp = sql.query("SELECT * FROM user LEFT JOIN gdp ON user.country = gdp.Country");
+-------------------+---+----------+---------+--------------------+------+---------------+------------------+---------+----------+---------+--------------------+--------------------+---------+----------+-----+--------+
| registration_dttm| id|first_name|last_name| email|gender| ip_address| cc| country| birthdate| salary| title| comments| Country|GDP Growth| Debt|Interest|
+-------------------+---+----------+---------+--------------------+------+---------------+------------------+---------+----------+---------+--------------------+--------------------+---------+----------+-----+--------+
|2016-02-03T07:55:29| 1| Amanda| Jordan| ajordan0@com.com|Female| 1.197.201.2| 6759521864920116|Indonesia| 3/8/1971| 49756.53| Internal Auditor| 1E+02|Indonesia| 6.5| 26.2| 7.7|
|2016-02-03T17:04:03| 2| Albert| Freeman| afreeman1@is.gd| Male| 218.111.175.34| | Canada| 1/16/1968|150280.17| Accountant IV| | Canada| 2.5| 52.5| 9.5|
|2016-02-03T07:22:34| 6| Kathryn| White| kwhite5@google.com|Female| 195.131.81.179| 3583136326049310|Indonesia| 2/25/1983| 69227.11| Account Executive| |Indonesia| 6.5| 26.2| 7.7|
|2016-02-03T08:33:08| 7| Samuel| Holmes|sholmes6@foxnews.com| Male| 232.234.81.197| 3582641366974690| Portugal|12/18/1987| 14247.62|Senior Financial ...| | Portugal| -1.6| 92.5| 9.7|
|2016-02-03T18:29:47| 10| Emily| Stewart|estewart9@opensou...|Female| 143.28.251.245| 3574254110301671| Nigeria| 1/28/1997| 27234.28| Health Coach IV| | Nigeria| 7.4| 3| 6.6|
|2016-02-03T08:53:23| 15| Dorothy| Hudson|dhudsone@blogger.com|Female| 8.59.7.0| 3542586858224170| Japan|12/20/1989|157099.71| Nurse Practicioner| alert('hi...| Japan| -0.6|174.8| 15.7|
|2016-02-03T00:44:01| 16| Bruce| Willis|bwillisf@bluehost...| Male|239.182.219.189| 3573030625927601| Brazil| |239100.65| | | Brazil| 2.7| 52.8| 24.1|
|2016-02-03T16:44:24| 18| Stephen| Wallace|swallaceh@netvibe...| Male| 152.49.213.62| 5433943468526428| Ukraine| 1/15/1978|248877.99|Account Represent...| | Ukraine| 5.2| 27.4| 5.2|
|2016-02-03T18:50:55| 23| Gregory| Barnes| gbarnesm@google.ru| Male| 220.22.114.145| 3538432455620641| Tunisia| 1/23/1971|182233.49|Senior Sales Asso...| 사회과학원 어학연구소| Tunisia| -2| 44| 5.8|
|2016-02-03T08:02:34| 26| Anthony| Lawrence|alawrencep@miitbe...| Male| 121.211.242.99|564182969714151470| Japan|12/10/1979|170085.81| Electrical Engineer| | Japan| -0.6|174.8| 15.7|
+-------------------+---+----------+---------+--------------------+------+---------------+------------------+---------+----------+---------+--------------------+--------------------+---------+----------+-----+--------+
990 more rows...
Sparse Dataset
The feature vectors could be very sparse. To save space, SparseDataset stores data in a list of lists (LIL) sparse matrix format. SparseDataset stores one list per row, where each entry stores a column index and value. Typically, these entries are kept sorted by column index for faster lookup.
SparseDataset is often used to construct the data matrix. Once the matrix is constructed, it is typically converted to a format, such as Harwell-Boeing column-compressed sparse matrix format, which is more efficient for matrix operations.
The class BinarySparseDataset is more efficient for binary sparse data. In BinarySparseDataset, each item is stored as an integer array, which are the indices of nonzero elements in ascending order.
Loading sparse files is covered under Data Import and Export: LibSVM, coordinate triple lists, and Harwell-Boeing.
To read and write files (CSV, Parquet, ARFF, JSON, sparse formats, and more), continue with Data Import and Export.