Indexes and Labels in C# QuickStart Sample

Illustrates how to use indexes to label the rows and columns of a data frame or matrix, or the elements of a vector in C#.

This sample is also available in: Visual Basic, F#.

Overview

This QuickStart sample demonstrates how to use indexes to label and organize data in vectors and matrices in Numerics.NET.

The sample shows several key aspects of working with indexes:

  • Creating indexes from arrays, numeric ranges, and date ranges
  • Using indexes to label vector elements and matrix rows/columns
  • Looking up positions of elements in an index both exactly and approximately
  • Working with automatic alignment of vectors based on their indexes
  • Handling calculations that preserve indexes through operations
  • Creating interval-based indexes for categorization

The code illustrates how indexes enable intuitive data manipulation by allowing operations to be performed based on labels rather than numeric positions. This is particularly useful when working with time series data, categorical data, or any scenario where meaningful labels improve code readability and reduce errors.

The sample also demonstrates how indexes propagate through calculations and how they can be used with matrices to provide meaningful row and column labels for better data organization and analysis.

The code

using System;

using Numerics.NET.DataAnalysis;
using Numerics.NET;

using Index = Numerics.NET.DataAnalysis.Index;


// Illustrates how to use indexes to label the elements
// of a vector, or the rows and columns of a matrix.


// The license is verified at runtime. We're using
// a 30 day trial key here. For more information, see
//     https://numerics.net/trial-key
Numerics.NET.License.Verify("your-trial-key-here");

//
// Indexes
//

// An index is a set of keys that can be used
// to label one or more dimensions of a vector,
// matrix, or data frame.

//
// Construction
//

// The simplest way to create an index is from an array:
var index = Index.Create(new[] { "a", "b", "c", "d" });
// We can then assign this to the Index property of a vector:
var v = Vector.Create(new double[] { 1.0, 2.0, 3.0, 4.0 });
v.Index = index;
Console.WriteLine(v);

// An index by position is very common,
// and can be created efficiently using the
// Default method:
var numbers = Index.Default(10); // 0, 1, ..., 9
var numbers2 = Index.Default(10, 20); // 10, 11, ..., 19

// Various options exist to create indexes over date ranges,
// for example:
var dateIndex = Index.CreateDateRange(new DateTime(2015, 4, 25), 10);
// 2015/4/25, 2015/4/26, ..., 2015/5/4

// Finally, for some purposes it may be useful to create
// an index of intervals, for example when you want to
// categorize people into age groups:
int[] ages = { 0, 18, 35, 65 };
var ageGroups = Index.CreateBins(ages, SpecialBins.AboveMaximum);

//
// Properties
//

// Indexes have a length
Console.WriteLine($"# of keys in index: {index.Length}");
// Indexes usually have unique elements.
Console.WriteLine($"Keys are unique? {index.IsUnique}");
// The elements may be sorted or not.
Console.WriteLine($"Keys are sorted? {index.IsSorted}");
Console.WriteLine($"Sort order: {index.SortOrder}");

//
// Lookup
//

// Once created, you can look up the position of a key:
var position = index.Lookup("c"); // = 2
if (index.TryLookup("e", out position))
    Console.WriteLine("We shouldn't be here.");

// You can also look up the nearest date.
var dates = Index.CreateDateRange(DateTime.Today.AddDays(-5), 10);
var now = DateTime.Now;
// An exact lookup fails in this case:
if (!dates.TryLookup(now, out position))
    Console.WriteLine("Exact lookup failed.");
// But looking for the nearest key works fine:
position = dates.LookupNearest(now, Direction.Backward); // = 5
position = dates.LookupNearest(now, Direction.Forward); // = 6

//
// Automatic alignment
//

// One of the useful features of indexes is that
// values are aligned on key values automatically.
// For example, given two vectors:
var a = Vector.Create(
    new[] { 1.0, 2.0, 3.0, 4.0 },
    new[] { "a", "b", "c", "d" });
var b = Vector.Create(
    new[] { 10.0, 30.0, 40.0, 50.0 },
    new[] { "a", "c", "d", "e" });
// We can compute their sum:
Console.WriteLine(a + b);
// and we find that elements are added
// when they have the same key,
// not when they have the same position.

// Indexes also propagate through calculations:
Console.WriteLine($"Exp(a) = \n{Vector.Exp(a)}");
Console.WriteLine($"a[a % 2 == 0] =\n{a[x => x % 2 == 0]}");

// Matrices can have a row and/or a column index:
var c = Matrix.CreateRandom(100, 4);
c.ColumnIndex = Index.Create(new[] { "a", "b", "c", "d" });
var cTc = c.Transpose() * c;
Console.WriteLine($"C^T*C = \n{cTc.Summarize()}");

Console.Write("Press any key to exit.");
Console.ReadLine();