# Finance

VTKExamples/Cxx/Modelling/Finance

### Description¶

The first step is to choose dependent and independent variables. This choice is essentially a mapping from multidimensional data into an unstructured point dataset. This example chooses MONTHLY_PAYMENT, INTEREST_RATE, and LOAN_AMOUNT as (x, y, z) point coordinates, and TIME_LATE as a scalar value. This maps four of six variables. For now we will ignore the other two variables.

The example uses vtkGaussianSplatter to perform the splatting operation (i.e., conversion from unstructured points to volume dataset). This is followed by an isosurface extraction. We splat the data two times. The first time we splat the entire population. This is to show context and appears as gray/ wireframe in the figure. The second time we splat the data and scale it by the value of TIME_LATE . As a result, only payments that are late contribute to the second isosurface. The results of this visualization are interesting. First, we see that there is a strong correlation between the two independent variables MONTHLY_PAYMENT and LOAN_AMOUNT . (This is more evident when viewing the data interactively.) We see that the data falls roughly on a plane at a 45 degree angle between these two axes. With a little reflection this is evident: the monthly payment is strongly a function of loan amount (as well as interest rate and payment period). Second, we see that there is a clustering of delinquent accounts within the total population. The cluster tends to grow with larger interest rates and shrink with smaller monthly payments and loan amounts. Although the relationship with interest rate is expected, the clustering towards smaller monthly payments is not. Thus our visualization has provided a clue into the data. Further exploration may reveal the reason(s), or we may perform additional data analysis and acquisition to understand the phenomena.

One important note about multidimensional visualization. Because we tend to combine variables in odd ways (e.g., the use of MONTHLY_PAYMENT , INTEREST_RATE , and LOAN_AMOUNT as (x, y, z) coordinates), normalization of the data is usually required. To normalize data we simply adjust data values to lie between (0,1). Otherwise our data can be badly skewed and result in poor visualizations.

Other Languages

See (Python)

Question

### Code¶

Finance.cxx

#include <vtkActor.h>
#include <vtkAxes.h>
#include <vtkCamera.h>
#include <vtkContourFilter.h>
#include <vtkDataSet.h>
#include <vtkFloatArray.h>
#include <vtkGaussianSplatter.h>
#include <vtkImageData.h>
#include <vtkNamedColors.h>
#include <vtkPointData.h>
#include <vtkPoints.h>
#include <vtkPolyDataMapper.h>
#include <vtkProperty.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
#include <vtkSmartPointer.h>
#include <vtkTubeFilter.h>
#include <vtkUnstructuredGrid.h>

#include <algorithm>
#include <array>
#include <string>

namespace {
vtkSmartPointer<vtkDataSet> ReadFinancialData(const char* fname, const char* x,
const char* y, const char* z,
const char* s);
int ParseFile(FILE* file, const char* tag, float* data);
} // namespace

int main(int argc, char* argv[])
{
double bounds[6];

if (argc < 2)
{
std::cout << "Usage: " << argv[0] << " financial_file" << endl;
return EXIT_FAILURE;
}
char* fname = argv[1];

auto colors = vtkSmartPointer<vtkNamedColors>::New();

// Set the color for the population.
std::array<unsigned char, 4> popColor{{230, 230, 230, 255}};
colors->SetColor("PopColor", popColor.data());

auto dataSet = ReadFinancialData(fname, "MONTHLY_PAYMENT", "INTEREST_RATE",
"LOAN_AMOUNT", "TIME_LATE");
// construct pipeline for original population
auto popSplatter = vtkSmartPointer<vtkGaussianSplatter>::New();
popSplatter->SetInputData(dataSet);
popSplatter->SetSampleDimensions(100, 100, 100);
popSplatter->ScalarWarpingOff();

auto popSurface = vtkSmartPointer<vtkContourFilter>::New();
popSurface->SetInputConnection(popSplatter->GetOutputPort());
popSurface->SetValue(0, 0.01);

auto popMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
popMapper->SetInputConnection(popSurface->GetOutputPort());
popMapper->ScalarVisibilityOff();

auto popActor = vtkSmartPointer<vtkActor>::New();
popActor->SetMapper(popMapper);
popActor->GetProperty()->SetOpacity(0.3);
popActor->GetProperty()->SetColor(colors->GetColor3d("popColor").GetData());

// construct pipeline for delinquent population
auto lateSplatter = vtkSmartPointer<vtkGaussianSplatter>::New();
lateSplatter->SetInputData(dataSet);
lateSplatter->SetSampleDimensions(50, 50, 50);
lateSplatter->SetScaleFactor(0.005);

auto lateSurface = vtkSmartPointer<vtkContourFilter>::New();
lateSurface->SetInputConnection(lateSplatter->GetOutputPort());
lateSurface->SetValue(0, 0.01);

auto lateMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
lateMapper->SetInputConnection(lateSurface->GetOutputPort());
lateMapper->ScalarVisibilityOff();

auto lateActor = vtkSmartPointer<vtkActor>::New();
lateActor->SetMapper(lateMapper);
lateActor->GetProperty()->SetColor(colors->GetColor3d("Red").GetData());

// create axes
popSplatter->Update();
popSplatter->GetOutput()->GetBounds(bounds);

auto axes = vtkSmartPointer<vtkAxes>::New();
axes->SetOrigin(bounds[0], bounds[2], bounds[4]);
axes->SetScaleFactor(popSplatter->GetOutput()->GetLength() / 5);

auto axesTubes = vtkSmartPointer<vtkTubeFilter>::New();
axesTubes->SetInputConnection(axes->GetOutputPort());
axesTubes->SetNumberOfSides(6);

auto axesMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
axesMapper->SetInputConnection(axesTubes->GetOutputPort());

auto axesActor = vtkSmartPointer<vtkActor>::New();
axesActor->SetMapper(axesMapper);

// graphics stuff
auto renderer = vtkSmartPointer<vtkRenderer>::New();

auto renWin = vtkSmartPointer<vtkRenderWindow>::New();

auto interactor = vtkSmartPointer<vtkRenderWindowInteractor>::New();
interactor->SetRenderWindow(renWin);

// set up renderer
renderer->SetBackground(colors->GetColor3d("Wheat").GetData());
renWin->SetSize(640, 480);

renderer->ResetCamera();
renderer->GetActiveCamera()->Dolly(1.3);
renderer->ResetCameraClippingRange();

// interact with data
renWin->Render();
interactor->Start();

return EXIT_SUCCESS;
}

namespace {
const char* x, const char* y,
const char* z, const char* s)
{
float xyz[3];
FILE* file;
int i, npts;
char tag[80];

if ((file = fopen(filename, "r")) == 0)
{
std::cerr << "ERROR: Can't open file: " << filename << std::endl;
return NULL;
}

int n = fscanf(file, "%79s %d", tag, &npts); // read number of points
if (n != 2)
{
std::cerr << "ERROR: Can't read file: " << filename << std::endl;
fclose(file);
return NULL;
}
// Check for a reasonable npts
if (npts <= 0)
{
std::cerr << "ERROR: Number of points must be greater that 0" << std::endl;
fclose(file);
return NULL;
}
// We arbitrarily pick a large upper limit on npts
if (npts > VTK_INT_MAX / 10)
{
std::cerr << "ERROR: npts (" << npts << ") is unreasonably large"
<< std::endl;
fclose(file);
return NULL;
}
auto dataSet = vtkSmartPointer<vtkUnstructuredGrid>::New();
float* xV = new float[npts];
float* yV = new float[npts];
float* zV = new float[npts];
float* sV = new float[npts];

if (!ParseFile(file, x, xV) || !ParseFile(file, y, yV) ||
!ParseFile(file, z, zV) || !ParseFile(file, s, sV))
{
std::cerr << "ERROR: Couldn't read data!" << std::endl;
delete[] xV;
delete[] yV;
delete[] zV;
delete[] sV;
fclose(file);
return NULL;
}

auto newPts = vtkSmartPointer<vtkPoints>::New();
auto newScalars = vtkSmartPointer<vtkFloatArray>::New();

for (i = 0; i < npts; i++)
{
xyz[0] = xV[i];
xyz[1] = yV[i];
xyz[2] = zV[i];
//     std::cout << xV[i] << " " << yV[i] << " " << zV[i] << std::endl;
newPts->InsertPoint(i, xyz);
newScalars->InsertValue(i, sV[i]);
}

dataSet->SetPoints(newPts);
dataSet->GetPointData()->SetScalars(newScalars);

// cleanup
delete[] xV;
delete[] yV;
delete[] zV;
delete[] sV;
fclose(file);

return dataSet;
}

int ParseFile(FILE* file, const char* label, float* data)
{
char tag[80];
int i, npts, readData = 0;
float min = VTK_FLOAT_MAX;
float max = (-VTK_FLOAT_MAX);

if (file == NULL || label == NULL)
return 0;

rewind(file);

if (fscanf(file, "%79s %d", tag, &npts) != 2)
{
std::cerr << "ERROR: IO Error " << __FILE__ << ":" << __LINE__ << std::endl;
return 0;
}
while (!readData && fscanf(file, "%79s", tag) == 1)
{
if (!strcmp(tag, label))
{
for (i = 0; i < npts; i++)
{
if (fscanf(file, "%f", data + i) != 1)
{
std::cerr << "ERROR: IO Error " << __FILE__ << ":" << __LINE__
<< std::endl;
return 0;
}
if (data[i] < min)
min = data[i];
if (data[i] > min)
max = data[i]; // bug here
// Should be:
// if ( data[i] > max ) max = data[i];
}
// normalize data
for (i = 0; i < npts; i++) data[i] = min + data[i] / (max - min);
}
else
{
for (i = 0; i < npts; i++)
{
if (fscanf(file, "%*f") != 0)
{
std::cerr << "ERROR: IO Error " << __FILE__ << ":" << __LINE__
<< std::endl;
return 0;
}
}
}
}

return 0;
else
return 1;
}
} // namespace


### CMakeLists.txt¶

cmake_minimum_required(VERSION 3.3 FATAL_ERROR)

project(Finance)

find_package(VTK COMPONENTS
vtkCommonColor
vtkCommonCore
vtkCommonDataModel
vtkFiltersCore
vtkFiltersGeneral
vtkImagingHybrid
vtkInteractionStyle
vtkRenderingContextOpenGL2
vtkRenderingCore
vtkRenderingFreeType
vtkRenderingGL2PSOpenGL2
vtkRenderingOpenGL2 QUIET)
if (NOT VTK_FOUND)
message("Skipping Finance: ${VTK_NOT_FOUND_MESSAGE}") return () endif() message (STATUS "VTK_VERSION:${VTK_VERSION}")
if (VTK_VERSION VERSION_LESS "8.90.0")
# old system
include(${VTK_USE_FILE}) add_executable(Finance MACOSX_BUNDLE Finance.cxx ) target_link_libraries(Finance PRIVATE${VTK_LIBRARIES})
else ()
# include all components
target_link_libraries(Finance PRIVATE ${VTK_LIBRARIES}) # vtk_module_autoinit is needed vtk_module_autoinit( TARGETS Finance MODULES${VTK_LIBRARIES}
)
endif ()


cd Finance/build


If VTK is installed:

cmake ..


If VTK is not installed but compiled on your system, you will need to specify the path to your VTK build:

cmake -DVTK_DIR:PATH=/home/me/vtk_build ..


Build the project:

make


and run it:

./Finance


WINDOWS USERS

Be sure to add the VTK bin directory to your path. This will resolve the VTK dll's at run time.