"Parallel Coordinates" Charts

Need help in implementing some specific function to your LightningChart Ultimate powered application? Post a question and get code snippets from other LightningChart Ultimate community members.

Moderator: Queue Moderators

Post Reply
deanius
Posts: 3
Joined: Thu Aug 21, 2014 7:24 pm
Location: Pleasanton, California

"Parallel Coordinates" Charts

Post by deanius » Thu Aug 21, 2014 8:19 pm

To visualize high-dimensional multivariate data, a "Parallel Coordinates" chart can be used. See Wikipedia's page for Parallel Coordinates here: http://en/wikipedia.org/wiki/Parallel_coordinates

I am wondering if anyone has suggestions for an easy way to implement such a chart using LightningChart, and/or if you already have an implementation that you wouldn't mind sharing, or at least the concepts of how you might go about implementing it with LightningChart.

I am interested in 2D multi-Y axis implementations and 3D multi-Z plane implementations.

User avatar
ArctionPasi
Posts: 1367
Joined: Tue Mar 26, 2013 10:57 pm
Location: Finland
Contact:

Re: "Parallel Coordinates" Charts

Post by ArctionPasi » Thu Aug 21, 2014 9:40 pm

Hi Deanius,

That can be done with LC. We'll first make a parallel coordinates 2D example by beginning of next week.
LightningChart Support Team, PT

User avatar
ArctionPasi
Posts: 1367
Joined: Tue Mar 26, 2013 10:57 pm
Location: Finland
Contact:

Re: "Parallel Coordinates" Charts

Post by ArctionPasi » Sat Aug 23, 2014 12:12 am

Here's an example of 2D parallel coordinates chart, made with LightningChart component. it's for WinForms. If you are using WPF, the code is almost identical.

Code: Select all

// Chart.
        private LightningChartUltimate m_chart;
        
        //Attribute count 
        private const int AttributeCount = 6;

        //List of persons
        private List<BasicPersonInfo> m_listPersons = new List<BasicPersonInfo>();


        /// <summary>
        /// Constructor.
        /// </summary>
        public ExampleParallelCoordinates()
        {
			m_chart = null;

            InitializeComponent();

            //Create person attributes data, some info gathered when visiting a nurse 
            m_listPersons.Add(new BasicPersonInfo(70, 175, EyeColor.Brown, 25, 125, 70));
            m_listPersons.Add(new BasicPersonInfo(85, 160, EyeColor.Blue, 48, 168, 92));
            m_listPersons.Add(new BasicPersonInfo(68, 169, EyeColor.Blue, 32, 151, 84));
            m_listPersons.Add(new BasicPersonInfo(90, 186, EyeColor.Gray, 40, 132, 77));
            m_listPersons.Add(new BasicPersonInfo(48, 155, EyeColor.Green, 22, 115, 68)); 

            CreateChart();
        }

        public enum EyeColor
        {
            Blue = 1,
            Brown,
            Green,
            Gray,
        }

        public class BasicPersonInfo
        {
            public double Weight_kg;
            public double Height_cm;
            public EyeColor ColorOfEyes;
            public int Age;
            public double BloodPressureSystolic_mmHg;
            public double BloodPressureDiastolic_mmHg;

            public BasicPersonInfo(double weight, double height, EyeColor colorOfEyes, int age, double bpSys, double bpDia)
            {
                this.Weight_kg = weight;
                this.Height_cm = height;
                this.ColorOfEyes = colorOfEyes;
                this.Age = age;
                this.BloodPressureSystolic_mmHg = bpSys; 
                this.BloodPressureDiastolic_mmHg = bpDia; 
            }

            public double[] GetValues()
            {
                return new double[] {this.Weight_kg, this.Height_cm, (double)this.ColorOfEyes, this.Age, this.BloodPressureSystolic_mmHg, this.BloodPressureDiastolic_mmHg};
            }
        }

        /// <summary>
        /// Create chart.
        /// </summary>
        private void CreateChart()
        {
            //Create new chart 
            m_chart = new LightningChartUltimate(LicenseKeys.LicenseKeyStrings.LightningChartUltimate);
            
            //Disable rendering, strongly recommended before updating chart properties
            m_chart.BeginUpdate(); 

            //Chart parent must be set
            m_chart.Parent = this;

            //Fill parent area with chart
            m_chart.Dock = DockStyle.Fill;

            //Chart name
            m_chart.Name = "Parallel coordinates chart";

            //Hide legend box
            m_chart.ViewXY.LegendBox.Visible = true;

            //Create an Y axis for each attribute
            m_chart.ViewXY.YAxes.Clear();
            double dPositionInterval = 100.0 / ((double) AttributeCount - 1.0); //interval of categories in range 0...100

            //Weight axis. This is the 'master' axis  
            AxisY axisYWeight = new AxisY(m_chart.ViewXY);
            axisYWeight.SetRange(40, 100);
            axisYWeight.Title.Text = "Weight / kg"; 
            axisYWeight.Position = m_chart.ViewXY.YAxes.Count * dPositionInterval;
            m_chart.ViewXY.YAxes.Add(axisYWeight);

            //Height axis, 'slave' axis
            AxisY axisYHeight = new AxisY(m_chart.ViewXY);
            axisYHeight.SetRange(140, 200);
            axisYHeight.Title.Text = "Height / cm";
            axisYHeight.Position = m_chart.ViewXY.YAxes.Count * dPositionInterval;
            m_chart.ViewXY.YAxes.Add(axisYHeight);

            //Eye color axis 'slave' axis
            AxisY axisYEyeColor = new AxisY(m_chart.ViewXY);
            axisYEyeColor.SetRange(1, 4);
            axisYEyeColor.Title.Text = "Eye color";
            axisYEyeColor.CustomTicks.AddRange(new List<CustomAxisTick>() 
            { 
                new CustomAxisTick (axisYEyeColor, 1, "Blue"),
                new CustomAxisTick(axisYEyeColor,2, "Brown"),
                new CustomAxisTick(axisYEyeColor,3, "Green"),
                new CustomAxisTick(axisYEyeColor,4, "Gray"),
            });
            axisYEyeColor.CustomTicksEnabled = true;
            axisYEyeColor.AutoFormatLabels = false; 
            axisYEyeColor.Position = m_chart.ViewXY.YAxes.Count * dPositionInterval;
            m_chart.ViewXY.YAxes.Add(axisYEyeColor);

            //Age axis 'slave' axis
            AxisY axisYAge = new AxisY(m_chart.ViewXY);
            axisYAge.SetRange(10, 90);
            axisYAge.Title.Text = "Age / years";
            axisYAge.Position = m_chart.ViewXY.YAxes.Count * dPositionInterval;
            m_chart.ViewXY.YAxes.Add(axisYAge);

            //Blood pressure, systolic, 'slave' axis
            AxisY axisYBpSys = new AxisY(m_chart.ViewXY);
            axisYBpSys.SetRange(60, 180);
            axisYBpSys.Title.Text = "Blood pressure (sys)/ mmHg ";
            axisYBpSys.Position = m_chart.ViewXY.YAxes.Count * dPositionInterval;
            m_chart.ViewXY.YAxes.Add(axisYBpSys);

            //Blood pressure, diastolic, 'slave' axis
            AxisY axisYBpDia = new AxisY(m_chart.ViewXY);
            axisYBpDia.SetRange(60, 180);
            axisYBpDia.Title.Text = "Blood pressure (dia)/ mmHg ";
            axisYBpDia.Position = m_chart.ViewXY.YAxes.Count * dPositionInterval;
            m_chart.ViewXY.YAxes.Add(axisYBpDia); 

            foreach(AxisY axisY in m_chart.ViewXY.YAxes)
            {
                axisY.MajorGrid.Visible = false;
                axisY.AxisThickness = 1;
                axisY.RangeChanged += new AxisBase.RangeChangedHandler(axisY_RangeChanged);
            }

            //Setup x-axis
            m_chart.ViewXY.XAxes[0].Visible = false; 
            m_chart.ViewXY.XAxes[0].SetRange(0, 5);
            
            //Disable X zooming and panning 
            m_chart.ViewXY.ZoomPanOptions.RectangleZoomDirectionLayered = RectangleZoomDirectionLayered.Vertical;
            m_chart.ViewXY.ZoomPanOptions.PanDirection = PanDirection.Vertical;
            m_chart.ViewXY.ZoomPanOptions.MouseWheelZooming = MouseWheelZooming.Vertical; 

            //Turn of automatic Y axis placement so we can place them manually with Position property
            m_chart.ViewXY.AxisLayout.YAxisAutoPlacement = YAxisAutoPlacement.Off;

            int iPerson = 0; 

            //Add series for each person and bind them to the first Y axis 
            foreach (BasicPersonInfo person in m_listPersons)
            {
                PointLineSeries pls = new PointLineSeries(m_chart.ViewXY, m_chart.ViewXY.XAxes[0], m_chart.ViewXY.YAxes[0]);
                pls.PointsVisible = true;
                pls.Title.Text = "Person" + (iPerson + 1).ToString();

                SeriesPoint[] aPoints = new SeriesPoint[AttributeCount]; 
                double[] attributeValues = person.GetValues(); 
                for(int iAttrib = 0; iAttrib < AttributeCount; iAttrib++)
                {
                    aPoints[iAttrib].X = iAttrib;
                    aPoints[iAttrib].Y = ScaleToMasterAxis(iAttrib, attributeValues[iAttrib]); 
                } 
                
                pls.LineStyle.Color = DefaultColors.SeriesForBlackBackground[iPerson];
                pls.PointStyle.Color1 = DefaultColors.SeriesForBlackBackground[iPerson]; 
                pls.Points = aPoints;

                //Add the created point line series into PointLineSeries list 
                m_chart.ViewXY.PointLineSeries.Add(pls);

                iPerson++;
            }

            //Allow chart rendering
            m_chart.EndUpdate();
        }

        /// <summary>
        /// Scale given attribute to master axis scale, by converting the attribute value to screen coordinate and then to master axis range 
        /// </summary>
        /// <param name="attributeIndex">Attribute index</param>
        /// <param name="value">Attribute value</param>
        /// <returns>Scaled value</returns>
        double ScaleToMasterAxis(int attributeIndex, double value)
        {
            AxisY masterAxis = m_chart.ViewXY.YAxes[0];
            AxisY attributeAxis = m_chart.ViewXY.YAxes[attributeIndex];
            float fYCoord = attributeAxis.ValueToCoord(value);
            double dYValueOnMasterAxis = 0;
            masterAxis.CoordToValue(fYCoord, out dYValueOnMasterAxis);
            return dYValueOnMasterAxis; 

        }

        void axisY_RangeChanged(double newMin, double newMax, AxisBase axis, ref bool cancelRendering)
        {
            m_chart.BeginUpdate();

            int iPerson = 0;
            foreach (BasicPersonInfo person in m_listPersons)
            {
                double[] attribValues = person.GetValues();
                PointLineSeries series = m_chart.ViewXY.PointLineSeries[iPerson];
                
                //Update all other attributes, to master axis scale
                for (int iAttrib = 1; iAttrib < AttributeCount; iAttrib++)
                {
                    series.Points[iAttrib].Y = ScaleToMasterAxis(iAttrib, attribValues[iAttrib]);
                }

                series.InvalidateData();
                iPerson++;
            }
            
            m_chart.EndUpdate(); 
        }
Parallel coordinates chart
Parallel coordinates chart
ParallelCoordinatesChart.jpg (253.88 KiB) Viewed 56291 times
The axes by categories are draggable and the view is zoomable vertically.

Does this help?
LightningChart Support Team, PT

deanius
Posts: 3
Joined: Thu Aug 21, 2014 7:24 pm
Location: Pleasanton, California

Re: "Parallel Coordinates" Charts

Post by deanius » Sat Aug 23, 2014 12:27 am

Yes, this helps out a lot. And, yes, we are using WPF.

I'm assuming that a 3D implementation wouldn't be too much different. If it's not too much trouble, can you put together a 3D sample as well? No hurry...

I'm really impressed by your quick response for a sample. Thank you very much.

User avatar
ArctionPasi
Posts: 1367
Joined: Tue Mar 26, 2013 10:57 pm
Location: Finland
Contact:

Re: "Parallel Coordinates" Charts

Post by ArctionPasi » Thu Aug 28, 2014 10:49 pm

Hi Deanius,

I made this kind of prototype...
3D Parallel Coordinates Chart
3D Parallel Coordinates Chart
ParallelCoordinatesChart3D.jpg (400.46 KiB) Viewed 56242 times
This is for WPF

Code: Select all

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Arction.WPF.LightningChartUltimate;
using Arction.WPF.LightningChartUltimate.Series3D;
using Arction.WPF.LightningChartUltimate.Views.View3D;
using Arction.WPF.LightningChartUltimate.Axes;

namespace WpfApplication2
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        //Chart
        private LightningChartUltimate m_chart = null;

        public MainWindow()
        {
            InitializeComponent();
            CreateChart();
        }

        /// <summary>
        /// Create chart.
        /// </summary>
        private void CreateChart()
        {
            //Create new chart 
            m_chart = new LightningChartUltimate();

            //Disable rendering, strongly recommended before updating chart properties
            m_chart.BeginUpdate();

            //Set active view
            m_chart.ActiveView = ActiveView.View3D;

            //Chart parent must be set
            gridMain.Children.Add(m_chart); 


            //Set camera, 3D world dimensions etc. 
            m_chart.View3D.Camera.RotationY -= 40;
            m_chart.View3D.Dimensions.Z = 150;
            m_chart.View3D.XAxisPrimary3D.Reversed = true;
            m_chart.View3D.ZAxisPrimary3D.Title.Visible = false;
            m_chart.View3D.LegendBox.ShowCheckboxes = false;

            //Set data 
            SetData();

            //Allow chart rendering
            m_chart.EndUpdate();

        }
        private void SetData()
        {

            //Disable rendering, strongly recommended before updating chart properties
            m_chart.BeginUpdate();

            View3D v = m_chart.View3D;

            //Clear existing series and their data
            v.PointLineSeries3D.Clear();

            int iAttribCount = 5;
            int iGroupCount = 10;
            int iLinesPerGroup = 50;

            double dVariationX = 40;
            double dVariationY = 30;
            double xMin = v.XAxisPrimary3D.Minimum;
            double xMax = v.XAxisPrimary3D.Maximum;
            double yMin = v.YAxisPrimary3D.Minimum;
            double yMax = v.YAxisPrimary3D.Maximum;
            double dXShiftPerGroup = (xMax - xMin) / (double)(iGroupCount + 1);

            Random rand = new Random();

            for (int iGroup = 0; iGroup < iGroupCount; iGroup++)
            {
                Color colorGroupLine = DefaultColors.SeriesForBlackBackgroundWPF[iGroup];

                for (int iLine = 0; iLine < iLinesPerGroup; iLine++)
                {
                    PointLineSeries3D series = new PointLineSeries3D(v, Axis3DBinding.Primary, Axis3DBinding.Primary, Axis3DBinding.Primary);
                    series.LineStyle.Color = colorGroupLine;
                    series.LineStyle.Width = 2;
                    series.LineStyle.AntiAliasing = LineAntialias.None;
                    series.PointsVisible = false;
                    series.Title.ShowInLegendBox = iLine == 0; //Only show the first line in the legend box of each group
                    series.Title.Text = "Data group " + (iGroup + 1).ToString();
                    v.PointLineSeries3D.Add(series);

                    PointDouble3D[] points = new PointDouble3D[iAttribCount];
                    for (int i = 0; i < iAttribCount; i++)
                    {
                        points[i].X = (double)(iGroup + 1) * dXShiftPerGroup + dVariationX / 2.0 * (rand.NextDouble() - 0.5) + (rand.NextDouble() - 0.5) * Math.Sin((double)iLine);
                        points[i].Y = 30 + dVariationY * rand.NextDouble() + dVariationY * Math.Sin(iGroup / 3.0) * Math.Sin((double)i * 5.0);// (Math.Sin((double)i/3.0) * (double)(iGroup) / 3.0 *Math.Sin(iGroup)/(double)iGroupCount);
                        points[i].Z = i;
                    }

                    series.Points = points;
                }
            }

            //Set Z axis range according to attribute count
            Axis3DBase zAxis = v.ZAxisPrimary3D;
            zAxis.SetRange(-0.5, iAttribCount - 0.5);

            //Set custom ticks to explain the attributes and planes 
            for (int i = 0; i < iAttribCount; i++)
            {
                zAxis.CustomTicks.Add(new CustomAxisTick(zAxis, i, "Attribute " + (i + 1).ToString()));
            }
            zAxis.CustomTicksEnabled = true;
            zAxis.AutoFormatLabels = false;


            //Create XY plane for each attribute
            v.Rectangles.Clear();

            for (int i = 0; i < iAttribCount; i++)
            {
                Rectangle3D rect = new Rectangle3D(v, Axis3DBinding.Primary, Axis3DBinding.Primary, Axis3DBinding.Primary);

                rect.Fill.Color = Color.FromArgb(120, 64, 64, 64);

                //These are 3D world coordinates
                rect.Center.X = (xMax + xMin) / 2.0;
                rect.Center.Y = (yMax + yMin) / 2.0;
                rect.Center.Z = i;

                rect.Size.Width = v.Dimensions.X;
                rect.Size.Height = v.Dimensions.Y;

                rect.Rotation.X = 90;

                v.Rectangles.Add(rect);
            }

            //Allow chart rendering
            m_chart.EndUpdate();
        }
    }
}

I hope this helps... :roll:
LightningChart Support Team, PT

deanius
Posts: 3
Joined: Thu Aug 21, 2014 7:24 pm
Location: Pleasanton, California

Re: "Parallel Coordinates" Charts

Post by deanius » Thu Aug 28, 2014 11:17 pm

Yes, this is exactly what I was looking for to get an idea of what it would take to implement Parallel coordinates in 3D space.
Thanks again.

usmanwaheed
Posts: 6
Joined: Tue Jul 23, 2019 5:52 am

Re: "Parallel Coordinates" Charts

Post by usmanwaheed » Tue Jul 23, 2019 5:58 am

namespace ParallelCoordinates
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
///

public partial class MainWindow : Window
{


//List of persons
private List<BasicPersonInfo> m_listPersons = new List<BasicPersonInfo>();
private int AttributeCount = 6;

public MainWindow()
{

InitializeComponent();

//Create person attributes data, some info gathered when visiting a nurse
m_listPersons.Add(new BasicPersonInfo(70, 175, EyeColor.Brown, 25, 125, 70));
m_listPersons.Add(new BasicPersonInfo(85, 160, EyeColor.Blue, 48, 168, 92));
m_listPersons.Add(new BasicPersonInfo(68, 169, EyeColor.Blue, 32, 151, 84));
m_listPersons.Add(new BasicPersonInfo(90, 186, EyeColor.Gray, 40, 132, 77));
m_listPersons.Add(new BasicPersonInfo(48, 155, EyeColor.Green, 22, 115, 68));

CreateChart();
}

private void CreateChart()
{

m_chart.BeginUpdate();

//Chart parent must be set
// m_chart.Parent = this;

//Fill parent area with chart
//(Content as Grid).Children.Add(m_chart); ;

//Chart name
// m_chart.Name = "Parallel coordinates chart";

//Hide legend box
m_chart.ViewXY.AutoSpaceLegendBoxes = true;

//Create an Y axis for each attribute
m_chart.ViewXY.YAxes.Clear();
double dPositionInterval = 100.0 / ((double)AttributeCount - 1.0); //interval of categories in range 0...100

//Weight axis. This is the 'master' axis
AxisY axisYWeight = new AxisY(m_chart.ViewXY);
axisYWeight.SetRange(40, 100);
axisYWeight.Title.Text = "Weight / kg";
axisYWeight.Position = m_chart.ViewXY.YAxes.Count * dPositionInterval;
m_chart.ViewXY.YAxes.Add(axisYWeight);

//Height axis, 'slave' axis
AxisY axisYHeight = new AxisY(m_chart.ViewXY);
axisYHeight.SetRange(140, 200);
axisYHeight.Title.Text = "Height / cm";
axisYHeight.Position = m_chart.ViewXY.YAxes.Count * dPositionInterval;
m_chart.ViewXY.YAxes.Add(axisYHeight);

//Eye color axis 'slave' axis
AxisY axisYEyeColor = new AxisY(m_chart.ViewXY);
axisYEyeColor.SetRange(1, 4);
axisYEyeColor.Title.Text = "Eye color";
axisYEyeColor.CustomTicks.AddRange(new List<CustomAxisTick>()
{
new CustomAxisTick (axisYEyeColor, 1, "Blue"),
new CustomAxisTick(axisYEyeColor,2, "Brown"),
new CustomAxisTick(axisYEyeColor,3, "Green"),
new CustomAxisTick(axisYEyeColor,4, "Gray"),
});
axisYEyeColor.CustomTicksEnabled = true;
axisYEyeColor.AutoFormatLabels = false;
axisYEyeColor.Position = m_chart.ViewXY.YAxes.Count * dPositionInterval;
m_chart.ViewXY.YAxes.Add(axisYEyeColor);

//Age axis 'slave' axis
AxisY axisYAge = new AxisY(m_chart.ViewXY);
axisYAge.SetRange(10, 90);
axisYAge.Title.Text = "Age / years";
axisYAge.Position = m_chart.ViewXY.YAxes.Count * dPositionInterval;
m_chart.ViewXY.YAxes.Add(axisYAge);

//Blood pressure, systolic, 'slave' axis
AxisY axisYBpSys = new AxisY(m_chart.ViewXY);
axisYBpSys.SetRange(60, 180);
axisYBpSys.Title.Text = "Blood pressure (sys)/ mmHg ";
axisYBpSys.Position = m_chart.ViewXY.YAxes.Count * dPositionInterval;
m_chart.ViewXY.YAxes.Add(axisYBpSys);

//Blood pressure, diastolic, 'slave' axis
AxisY axisYBpDia = new AxisY(m_chart.ViewXY);
axisYBpDia.SetRange(60, 180);
axisYBpDia.Title.Text = "Blood pressure (dia)/ mmHg ";
axisYBpDia.Position = m_chart.ViewXY.YAxes.Count * dPositionInterval;
m_chart.ViewXY.YAxes.Add(axisYBpDia);

foreach (AxisY axisY in m_chart.ViewXY.YAxes)
{
axisY.MajorGrid.Visible = false;
axisY.AxisThickness = 1;
axisY.RangeChanged += AxisY_RangeChanged;
//axisY.RangeChanged += new AxisBase.RangeChangedHandler(axisY_RangeChanged);
}

//Setup x-axis
m_chart.ViewXY.XAxes[0].Visible = false;
m_chart.ViewXY.XAxes[0].SetRange(0, 5);

//Disable X zooming and panning
m_chart.ViewXY.ZoomPanOptions.RectangleZoomDirection = RectangleZoomDirection.Vertical;
m_chart.ViewXY.ZoomPanOptions.PanDirection = PanDirection.Vertical;
m_chart.ViewXY.ZoomPanOptions.MouseWheelZooming = MouseWheelZooming.Vertical;

//Turn of automatic Y axis placement so we can place them manually with Position property
m_chart.ViewXY.AxisLayout.YAxisAutoPlacement = YAxisAutoPlacement.Off;

int iPerson = 0;

//Add series for each person and bind them to the first Y axis
foreach (BasicPersonInfo person in m_listPersons)
{
PointLineSeries pls = new PointLineSeries(m_chart.ViewXY, m_chart.ViewXY.XAxes[0], m_chart.ViewXY.YAxes[0]);
pls.PointsVisible = true;
pls.Title.Text = "Person" + (iPerson + 1).ToString();

SeriesPointCollection aPoints = new SeriesPointCollection();
double[] attributeValues = person.GetValues();

for (int iAttrib = 0; iAttrib < AttributeCount; iAttrib++)
{
SeriesPoint seriesPoint = new SeriesPoint();
seriesPoint.X = iAttrib;
seriesPoint.Y = ScaleToMasterAxis(iAttrib, attributeValues[iAttrib]);
aPoints.Add(seriesPoint);
}

pls.LineStyle.Color = DefaultColors.SeriesForBlackBackgroundWpf[iPerson];
pls.PointStyle.Color1 = DefaultColors.SeriesForBlackBackgroundWpf[iPerson];
pls.Points = aPoints;

//Add the created point line series into PointLineSeries list
m_chart.ViewXY.PointLineSeries.Add(pls);

iPerson++;
}

//Allow chart rendering
m_chart.EndUpdate();
}

private void AxisY_RangeChanged(object sender, RangeChangedEventArgs e)
{
m_chart.BeginUpdate();

int iPerson = 0;
foreach (BasicPersonInfo person in m_listPersons)
{
double[] attribValues = person.GetValues();
PointLineSeries series = m_chart.ViewXY.PointLineSeries[iPerson];

//Update all other attributes, to master axis scale
for (int iAttrib = 1; iAttrib < AttributeCount; iAttrib++)
{
series.Points[iAttrib].Y = ScaleToMasterAxis(iAttrib, attribValues[iAttrib]);
}

series.InvalidateData();
iPerson++;
}
}

public class BasicPersonInfo
{
public double Weight_kg;
public double Height_cm;
public EyeColor ColorOfEyes;
public int Age;
public double BloodPressureSystolic_mmHg;
public double BloodPressureDiastolic_mmHg;

public BasicPersonInfo(double weight, double height, EyeColor colorOfEyes, int age, double bpSys, double bpDia)
{
this.Weight_kg = weight;
this.Height_cm = height;
this.ColorOfEyes = colorOfEyes;
this.Age = age;
this.BloodPressureSystolic_mmHg = bpSys;
this.BloodPressureDiastolic_mmHg = bpDia;
}

public double[] GetValues()
{
return new double[] { this.Weight_kg, this.Height_cm, (double)this.ColorOfEyes, this.Age, this.BloodPressureSystolic_mmHg, this.BloodPressureDiastolic_mmHg };
}
}
public enum EyeColor
{
Blue = 1,
Brown,
Green,
Gray,
}
double ScaleToMasterAxis(int attributeIndex, double value)
{
AxisY masterAxis = m_chart.ViewXY.YAxes[0];
AxisY attributeAxis = m_chart.ViewXY.YAxes[attributeIndex];
float fYCoord = attributeAxis.ValueToCoord(value);
double dYValueOnMasterAxis = 0;
masterAxis.CoordToValue(fYCoord, out dYValueOnMasterAxis);
return dYValueOnMasterAxis;
}

}
}


This is my code I am trying to make Parallel coordinates but my Scaletomatrix is always returning 100 due to which lines are not creating please help me

Arction_LasseP
Posts: 141
Joined: Wed Mar 27, 2019 1:05 pm

Re: "Parallel Coordinates" Charts

Post by Arction_LasseP » Tue Jul 23, 2019 7:52 am

Hello,

We checked the code you posted and managed to get it working. The main issue that stopped it from working was that there is m_chart.BeginUpdate() inside the AxisY_RangeChanged -event but no EndUpdate(). This means that every time axis range is changed, the chart disables rendering, and since it never properly enables it, the PointLineSeries defined inside the event most likely won't get rendered or updated at all. InvalidateData() called inside the event notifies the chart that there is something new to render but doesn't help here as the rendering is disabled. The solution for this is to either add m_chart.EndUpdate() or remove the BeginUpdate().

Another small note is the lack of AfterRendering -event that was used in our original parallel coordinates example. Without it the series might not be rendered unless there is some interaction with the axes.

Hope this helps.
Best regards,
Lasse

usmanwaheed
Posts: 6
Joined: Tue Jul 23, 2019 5:52 am

Re: "Parallel Coordinates" Charts

Post by usmanwaheed » Tue Jul 23, 2019 11:13 pm

private void AxisY_RangeChanged(object sender, RangeChangedEventArgs e)
{
m_chart.BeginUpdate();

int iPerson = 0;
foreach (BasicPersonInfo person in m_listPersons)
{
double[] attribValues = person.GetValues();
PointLineSeries series = m_chart.ViewXY.PointLineSeries[iPerson];

//Update all other attributes, to master axis scale
for (int iAttrib = 1; iAttrib < AttributeCount; iAttrib++)
{
series.Points[iAttrib].Y = ScaleToMasterAxis(iAttrib, attribValues[iAttrib]);
}

series.InvalidateData();
iPerson++;

}
m_chart.EndUpdate();
}


I have tried this and the other way around by removing m_chart.BeginUpdate(); the lines are not generating.....

usmanwaheed
Posts: 6
Joined: Tue Jul 23, 2019 5:52 am

Re: "Parallel Coordinates" Charts

Post by usmanwaheed » Wed Jul 24, 2019 12:21 am

I also don't understand where are you using afterrendering?

usmanwaheed
Posts: 6
Joined: Tue Jul 23, 2019 5:52 am

Re: "Parallel Coordinates" Charts

Post by usmanwaheed » Wed Jul 24, 2019 6:06 am

This is the outcome i am getting.
Attachments
This is outcome i am getting
This is outcome i am getting
pic.PNG (117.42 KiB) Viewed 41169 times

Arction_LasseP
Posts: 141
Joined: Wed Mar 27, 2019 1:05 pm

Re: "Parallel Coordinates" Charts

Post by Arction_LasseP » Wed Jul 24, 2019 6:53 am

Hello,

If you check our ParallelCoordinates -demo source code, you can see that it adds the PointLineSeries to the chart inside AfterRendering -event, not in CreateChart() -method as in your example code. Usually it is better to add the series when creating the chart, but this particular example is a special case; it uses screen coordinates to define the positions of the PointLineSeries. To use screen coordinates, the chart has to be rendered first.

Code: Select all

// In CreateChart() subscribe to the event
m_chart.AfterRendering += m_chart_AfterRendering;

private void m_chart_AfterRendering(object sender, AfterRenderingEventArgs e)
        {
            // We no longer need to handle this event so unsubscibe from it.
            m_chart.AfterRendering -= m_chart_AfterRendering;

            m_chart.BeginUpdate();

            int iPerson = 0;

            //Add series for each person and bind them to the first Y axis
            foreach (BasicPersonInfo person in m_listPersons)
            {
                PointLineSeries pls = new PointLineSeries(m_chart.ViewXY, m_chart.ViewXY.XAxes[0], m_chart.ViewXY.YAxes[0]);
                pls.PointsVisible = true;
                pls.Title.Text = "Person" + (iPerson + 1).ToString();

                //SeriesPointCollection aPoints = new SeriesPointCollection();
                SeriesPoint[] aPoints = new SeriesPoint[AttributeCount];
                double[] attributeValues = person.GetValues();

                for (int iAttrib = 0; iAttrib < AttributeCount; iAttrib++)
                {
                    SeriesPoint seriesPoint = new SeriesPoint();
                    seriesPoint.X = iAttrib;
                    seriesPoint.Y = ScaleToMasterAxis(iAttrib, attributeValues[iAttrib]);
                    //aPoints.Add(seriesPoint);
                    aPoints[iAttrib] = seriesPoint;
                }

                pls.LineStyle.Color = DefaultColors.SeriesForBlackBackgroundWpf[iPerson];
                pls.PointStyle.Color1 = DefaultColors.SeriesForBlackBackgroundWpf[iPerson];
                pls.Points = aPoints;

                //Add the created point line series into PointLineSeries list
                m_chart.ViewXY.PointLineSeries.Add(pls);

                iPerson++;
            }
            m_chart.EndUpdate();
        }
Note that we unsubscribe from the event immediately after it's been triggered, as it is no longer needed.

Another possible reason for this issue is that in your code a collection is used instead of an array:
SeriesPointCollection aPoints = new SeriesPointCollection();

Unless you are using WPF Fully-bindable platform instead of for example WPF Non-bindable, it is better to use arrays to assign data points as shown also in the code above.

Best regards,
Lasse

usmanwaheed
Posts: 6
Joined: Tue Jul 23, 2019 5:52 am

Re: "Parallel Coordinates" Charts

Post by usmanwaheed » Thu Jul 25, 2019 11:37 pm

Thank you so much for your help that works for me. Just one more thing in your example above you are showing titles with person 1 , person 2 and so on how are you doing it as i am following exactly the same code but it does not come up in my code.

usmanwaheed
Posts: 6
Joined: Tue Jul 23, 2019 5:52 am

Re: "Parallel Coordinates" Charts

Post by usmanwaheed » Thu Jul 25, 2019 11:49 pm

Plus if I wanted to add some lines with same color how can i do that where are you handling the color because when I add the one more line with same eye color it always create a new line with different color.

m_listPersons.Add(new BasicPersonInfo(70, 175, EyeColor.Brown, 25, 125, 70));
m_listPersons.Add(new BasicPersonInfo(60, 135, EyeColor.Brown, 15, 105, 50));
m_listPersons.Add(new BasicPersonInfo(85, 160, EyeColor.Blue, 48, 168, 92));
m_listPersons.Add(new BasicPersonInfo(68, 169, EyeColor.Blue, 32, 151, 84));
m_listPersons.Add(new BasicPersonInfo(90, 186, EyeColor.Gray, 40, 132, 77));
m_listPersons.Add(new BasicPersonInfo(48, 155, EyeColor.Green, 22, 115, 68));

ArctionKestutis
Posts: 549
Joined: Mon Mar 14, 2016 9:22 am

Re: "Parallel Coordinates" Charts

Post by ArctionKestutis » Fri Jul 26, 2019 6:50 am

Hi,

This start to look like we are building proof-of-concept application. This would be part of technical support which requires active LightningChart subscription. Therefore, I would like to ask you to send subscription ID directly to Arction Support (support [at] arction com). If you don't have one please introduce yourself (company you represent, how Chart is used etc.).

All the best.

Post Reply