1

I'm trying to figure out how I could use variables in Spotfire (online version) to build a scoring mechanism and populate a calculated column with the final result.

I have a couple of values stored in columns that I would use to evaluate and attribute a score like this:

if column1<10 then segment1 = segment1 + 1

if column1>10 then segment2 = segment2+1

...ETC...

In the end each "segment" should have a score and I would like to simply display the name of the segment that has the highest score.

Ex:

Segment1 has a final value of 10

Segment2 has a final value of 22

Segment3 has a final value of 122

I would display Segment3 as value for the calculated column

Using only "IF" would lead me to a complicated IF structure so I'm more looking for something that looks more like a script.

Is there a way to achieve this with Spotfire?

Thanks Laurent

Laurent
  • 1,465
  • 2
  • 18
  • 41
  • 1
    @Laurent- Approximately, how many conditions is this IF statement checking? – ksp585 Jun 26 '19 at 23:17
  • @ksp585 I have at least 3 to 4 conditions now and I may need to increase this number depending on the scoring results. – Laurent Jun 27 '19 at 08:21

1 Answers1

2

To cycle through the data rows and calculate a running score, you can use an IronPython script. The script below is reading the numeric data from Col1 and Col2 of a data table named "Data Table". It calculates a score value for each row and writes it to a tab delimited text string. When done, it adds it to the Spotfire table using the Add Columns function. Note, the existing data needs to have a unique identifier. If not, the RowId() function can be used to create a calculated column for a unique row id.

from Spotfire.Dxp.Data import *
from System.IO import StringReader, StreamReader, StreamWriter, MemoryStream, SeekOrigin
from Spotfire.Dxp.Data.Import import *
from System import Array

def add_column(table, text, col_name):
    # read the text data into memory
    mem_stream = MemoryStream()
    writer = StreamWriter(mem_stream)
    writer.Write(text)
    writer.Flush()
    mem_stream.Seek(0, SeekOrigin.Begin)

    # define the structure of the text data
    settings = TextDataReaderSettings()
    settings.Separator = "\t"
    settings.SetDataType(0, DataType.Integer)
    settings.SetColumnName(0, 'ID')
    settings.SetDataType(1, DataType.Real)
    settings.SetColumnName(1, col_name)

    # create a data source from the in memory text data
    data = TextFileDataSource(mem_stream, settings)

    # define the relationship between the existing table (left) and the new data (right)
    leftColumnSignature = DataColumnSignature("Store ID", DataType.Integer)
    rightColumnSignature = DataColumnSignature("ID", DataType.Integer)
    columnMap = {leftColumnSignature:rightColumnSignature}
    ignoredColumns = []
    columnSettings = AddColumnsSettings(columnMap, JoinType.LeftOuterJoin, ignoredColumns)

    # now add the column(s)
    table.AddColumns(data, columnSettings)

#get the data table
table=Document.Data.Tables["Data Table"]

#place data cursor on a specific column
cursorCol1 = DataValueCursor.CreateFormatted(table.Columns["Col1"])
cursorCol2 = DataValueCursor.CreateFormatted(table.Columns["Col2"])
cursorColId = DataValueCursor.CreateFormatted(table.Columns["ID"])
cursorsList = Array[DataValueCursor]([cursorCol1, cursorCol2, cursorColId])

text = ""
rowsToInclude = IndexSet(table.RowCount,True)
#iterate through table column rows to retrieve the values
for row in table.GetRows(rowsToInclude, cursorsList):
    score = 0
    # get the current values from the cursors
    col1Val = cursorCol1.CurrentDataValue.ValidValue
    col2Val = cursorCol2.CurrentDataValue.ValidValue
    id = cursorColId.CurrentDataValue.ValidValue
    # now apply rules for scoring
    if col1Val <= 3:
        score -= 3
    elif col1Val > 3 and col2Val > 50:
        score += 10
    else:
        score += 5
    text += "%d\t%f\r\n" % (id, score)

add_column(table, text, 'Score_Result')

For an approach with no scripting, but also no accumulation, you can use calculated columns. To get the scores, you can use a calculated column with case statements. For Segment 1, you might have:

case 
when [Col1] > 100 then 10
when [Col1] < 100 and [Col2] > 600 then 20
end

The, once you have the scores, you can create a calculated column, say [MaxSegment]. The expression for this will be Max([Segment1],[Segment2],[Segment3]...). Then display the value of [MaxSegment].

The max function in this case is acting as a row expression and is calculating the max value across the row of the columns given.

drxl
  • 364
  • 1
  • 7
  • thanks! That would be useful when I have built the score but I still need to populate the different columns with values and to do this, I would need something to build the score (through variables for example) – Laurent Jun 27 '19 at 08:22
  • 1
    @Laurent the simplest way to do this is to use calculated columns with case statements. I edited my answer above. – drxl Jun 27 '19 at 15:15
  • thanks! that makes sense but as I need to cumulate scores, I'll need to foresee a lot of difference cases. Each condition should add or remove points from the score, ex: if col1>10 then + 1 if col2<2 then +2 if col3<2 then -1 ...etc... when the case structure, I need to create one line per possible condition. I'll give it a try but if you think of something else, let me know :) – Laurent Jun 28 '19 at 09:44
  • 1
    Ah, the segment is accumulating, sorry I missed that. This will be an ironpython script. Give me a bit and I'll post it. – drxl Jun 28 '19 at 12:28
  • that's interesting, I didn't know this type of scripting was possible, thanks for pointing out! I found out where to use it but I still have one problem to solve. The scoring is calculated for each row and i would like to store the result for each row in a dedicated column (let's call it "Score_Result"). So if for one row I have a score of 5, I'd like to store the value in the same row in the column "Score_Result". Is that possible? Thanks! – Laurent Jul 02 '19 at 10:00
  • 1
    Updated to add a column of scores instead of just calculating one. You will need to add the ironpython script to the template and you can kick it off by clicking a button or changing a document property value. Look in the help for iron python scripting. – drxl Jul 02 '19 at 14:45