Monday, 22 January 2018

Introduction to R - Part 5 : Lists

Howdy geeks.....

We have covered quite a lot in R in the last few posts. Let's do a small Recap shall we.

Vector : 1 Dimensional data structure which can only hold a single data type

Matrix : 2 Dimensional data structure which can only hold a single data type. ( Remember the Matrix Revolution movie 💪 )

Factor : a categorical variable can only hold a limited number of categories. Eg: Blood types

Lists


Lists can have any data type in R. Let's see how we can create a List in R

# Numeric vector: 1 up to 10
geek_vector <- 1:10

# Numeric matrix: 1 up to 9
geek_matrix <- matrix(1:9, ncol = 3)

# Factor of sizes
geek_factor <- factor(c("M","S","L","L","M"), ordered = TRUE, levels = c("S","M","L"))

# Construct my_list with these different elements
geek_list <- list(geek_vector,geek_matrix,geek_factor)

You can define names by using the names() function.
names(geek_list) <- c("vec","mat","fac")

You can name the elements at the time of creating the list as well.

# Create actors and ratings
actors_vector <- c("Jack Nicholson","Shelley Duvall","Danny Lloyd","Scatman Crothers","Barry Nelson")
ratings_factor <- factor(c("Good", "OK", "Good", "Perfect", "Bad", "Perfect", "Good"),
                  ordered = TRUE, levels = c("Bad", "OK", "Good", "Perfect"))

# Create shining_list
shining_list <- list(title = "The Shining" ,actors = actors_vector , ratings= rating_factor  )

Subset and extend lists
Sub settling list is not similar to sub setting vectors.


Let's use the above code for our demonstrations. 

As you can see the list contains 4 elements. a Character , 2 numeric's and a list

Ok lets try song[1]
> song[1]
$title

[1] "Rsome times"

Now let's try song[[1]]
> song[[1]]
[1] "Rsome times"

When you use single square brackets it will return a list it self. If you use double square brackets it will return the element.
Lets say you want to get the title and the track as a list. You can use the below code to get that.> song[c(1,3)]
$title
[1] "Rsome times"
$track
[1] 5

 So what if we use double brackets here ??? Lets try that out
> song[[c(1,3)]]
Error in song[[c(1, 3)]] : subscript out of bounds

As you can see it generates an index out of bounds error. It looks strange isn't it . Well actually you can only use double brackets to get single elements. When you use double brackets in the about code it actually represents

song[[c(1,3)]] == song[[1]][[3]]

It means take the 1st element of the song list and from that take the 3rd element. Since the first element is a character vector with length 1 and there are no more elements in that.

But this will work with the 4th element which is a list. Let's try that out.

> song[[4]][[1]]
[1] "R you on time?"

It returns the title of the list.

Sub set by name
It is pretty straight forward. You can use the name to select the element you require.

Eg: song[["duration"]]
> song[["duration"]]
[1] 190
> song["duration"]
$duration
[1] 190

In this case both single and double brackets works the same way.

You could also use multiple elements as well.

> song[c("duration","similar")]
$duration
[1] 190

$similar
$similar$title
[1] "R you on time?"

$similar$duration
[1] 230

Sub set Logicals
Sub setting by Logicals will only work with single brackets.

> song[c(FALSE,TRUE,TRUE,FALSE)]
$duration
[1] 190

$track
[1] 5

Let's see what happens when we use double brackets.

> song[[c(FALSE,TRUE,TRUE,FALSE)]]
Error in song[[c(FALSE, TRUE, TRUE, FALSE)]] : 

  attempt to select less than one element in integerOneIndex

Returns as error. When we use double brackets it actually interprets like below

song[[F]][[T]][[T]][[F]] 
Well it doesn't make any sense right!!!!!!!

$ and extending
Another way to select elements in a list is to use the $ sign
> song$duration

[1] 190

You can also assign values using the $ sign.

Lets create a friends vector
> friends <- c("Kurt","Steve","John","Jane")

Now lets assign it to a new element named Sent.
> song$sent <- friends  // this can be also done by using --- > song[["sent"]] <- friends

Now let's look at our list
> song
$title
[1] "Rsome times"

$duration
[1] 190

$track
[1] 5

$similar
$similar$title
[1] "R you on time?"

$similar$duration
[1] 230

$sent

[1] "Kurt"  "Steve" "John"  "Jane"

Note : use of the str function provided more efficient details 💃

> str(song)
List of 5
 $ title   : chr "Rsome times"
 $ duration: num 190
 $ track   : num 5
 $ similar :List of 2
  ..$ title   : chr "R you on time?"
  ..$ duration: num 230

 $ sent    : chr [1:4] "Kurt" "Steve" "John" "Jane"

So to access the title element in the similar list of the song list you can do something like this.
> song$similar$title
[1] "R you on time?"

Wrap up

  • [[ or [ ?
    • [[ to select a single element
    • [ to select a sub list
  • [[ and $
    • Both of these can be used to sub set as well as add elements to the list



Sunday, 21 January 2018

Introduction to R - Part 4 : Factors


Factors


If you have a background on Statistics you may have heard about categorical variables. Well maybe not.

Unlike numerical variables , a categorical variable can only hold a limited number of categories. In R there is a specific data structure for these variables which is known as Factors.

One of the examples for a Factor is the blood type.

Lets see how we can create a Factor in R.

Create Factor
We will first create a vector called blood_vector and create the Factor out of it.

blood_vector
[1] "B"  "AB" "O"  "A"  "O"  "O"  "A"  "B"

blood_factor <- factor(blood_vector)
blood_factor
[1] B  AB O  A  O  O  A  B
Levels: A AB B O

The Output looks different than the original one: there are no double quotes and also if you notice there is something called factor levels, these levels are corresponding to the different categories.

R performs 2 things when you call the factor function on a character vector:

  1. It scans through the vector to check the different categories that are in there. In out example that's "A", "AB", "B" and "O". Notice here that R sorts the levels alphabetically. 
  2. Then , it converts the character vector,to a vector of integers. These integers correspond to a set of character values to use when the factor is displayed. 

Lets look at the structure of the Factor to determine this.

> str(blood_factor)
 Factor w/ 4 levels "A","AB","B","O": 3 2 4 1 4 4 1 3

So in this example there are 4 unique categories.

Rename Factor Labels
To set labels you can use the levels functions. This is similar to the names function for vectors.
> levels(blood_factor) <- c("BT_A","BT_AB","BT_B","BT_O")
> blood_factor
[1] BT_B  BT_AB BT_O  BT_A  BT_O  BT_O  BT_A  BT_B
Levels: BT_A BT_AB BT_B BT_O

You could also do this at the time of defining the factor. You can us e the labels function to this as well.
> factor(blood_vector, labels = c("BT_A","BT_AB","BT_B","BT_O"))
[1] BT_B  BT_AB BT_O  BT_A  BT_O  BT_O  BT_A  BT_B
Levels: BT_A BT_AB BT_B BT_O

For both of these options you need to make sure you set the labels according to the order. This might be tricky because if the order was different then incorrect labels would be set for the elements.
To solve this problem you can manually set the levels as well as the labels when defining the factor.
> factor(blood_vector,
levels = c("O","A","B","AB") ,
labels = c("BT_O","BT_A","BT_B","BT_AB"))

[1] BT_B  BT_AB BT_O  BT_A  BT_O  BT_O  BT_A  BT_B
Levels: BT_O BT_A BT_B BT_AB

Nominal versus Ordinal
Nominal categorical variables will not have an implied order. Example blood type A is not greater than or less then blood type O.

Such comparison will result in a warning. Let's try that out in R.

> blood_factor[1] < blood_factor[2]
[1] NA
Warning message:
In Ops.factor(blood_factor[1], blood_factor[2]) :
  ‘<’ not meaningful for factors

But there are example where the order is required. Example is T shirt sizes. L > M > S

vector_tshirt <- c("M","L","S","S","L","M","L","M")

> factor_tshrt <- factor(vector_tshirt , ordered = TRUE , levels = c("S","M","L"))
> factor_tshrt
[1] M L S S L M L M
Levels: S < M < L

If we now try to do a comparison it will result TRUE or FALSE.

> factor_tshrt[1] < factor_tshrt[2]
[1] TRUE

Summary

  • Factors are used to store categorical variable in R
  • Factors are integer vectors
  • You can change the factor levels using the levels function or the labels argument in the factor function
  • R allows you to make the difference between ordered and non-ordered factors by catering to nominal and ordinal variables.


Sunday, 14 January 2018

Introduction to R - Part 3 : Matrices

Introduction to Matrix



In my previous post We spoke about Vectors. Today we are going to talk about Matrices.

I get reminded of the movie Matrix. Well I think we can relate R Matrix to the movie Matrix. When I first started watching the Matrix movie , I was clueless and it did not make sense at all. I think it was the case for most fans. But then I watched it couple of times and I finally managed to understand what it was about.

R- Matrix might be a similar experience for you. Once you start playing with it you get the hang of it.



What is a Matrix ?? 

Well it's like the brother of the Vector. A vector is a one Dimensional structure. Where as a Matrix is a 2 Dimensional structure arranged into a fixed number of Rows and Columns.

Lets look at the syntax of creating a matrix.

> m <- matrix(1:6, byrow =TRUE, nrow = 2)
> m
     [,1] [,2] [,3]
[1,]    1    2    3
[2,]    4    5    6
> m <- matrix(1:6, nrow = 2)
> m
     [,1] [,2] [,3]
[1,]    1    3    5
[2,]    2    4    6

Notebyrow =TRUE means the data arranged row wise first.

Same way you can define a matrix and specify it column wise.

> matrix(1:6,ncol=2)
       [,1] [,2]
[1,]    1    4
[2,]    2    5
[3,]    3    6

Matrix Recycling



You can also define a matrix specifying the number rows and columns

> matrix(1:3,nrow= 2, ncol = 3)
      [,1] [,2] [,3]
[1,]    1    3    2
[2,]    2    1    3

Here as you can see even though the range has 3 elements the matrix will repeat it to automatically recycle it.



When you want to put a 4-element vector in a 6-element matrix, R generates a warning.

> matrix(1:4,nrow = 2, ncol = 3)
     [,1] [,2] [,3]
[1,]    1    3    1
[2,]    2    4    2
Warning message:
In matrix(1:4, nrow = 2, ncol = 3) :
  data length [4] is not a sub-multiple or multiple of the number of columns [3]


Apart from the Matrix() function there is a more easy way to create matrices using cbind() or rbind()

cbind aka Column bind
> cbind(1:3,4:6)
       [,1] [,2]
[1,]    1    4
[2,]    2    5
[3,]    3    6

cbind() function adds columns to the matrix

rbind aka Row bind
> rbind(1:3,4:6)
     [,1] [,2] [,3]
[1,]    1    2    3

[2,]    4    5    6

rbind() function adds rows to the matrix

These functions can be used to add a row or a column to an existing matrix.

Lets first look at our matrix
> m
      [,1] [,2] [,3]
[1,]    1    2    3
[2,]    4    5    6

Now lets add a new row with values 7 to 9
> rbind(m,7:9)
     [,1] [,2] [,3]
[1,]    1    2    3
[2,]    4    5    6
[3,]    7    8    9

Now lets add a new column with values 10 and 11.
> cbind(m,c(10:11))
       [,1] [,2] [,3] [,4]
[1,]    1    2    3   10
[2,]    4    5    6   11

Naming a Matrix
 In the case of vectors remember we used the names() function to name the elements. For matrix we can use the functions rownames() and colnames()

rownames() 

> rownames(m) <- c("Row1","Row2")
> m
          [,1] [,2] [,3]
Row1    1    2    3
Row2    4    5    6

colnames()

> colnames(m) <- c("Col1","Col2","Col3")
> m
        Col1 Col2 Col3
Row1    1    2    3
Row2    4    5    6

You can also name the rows and columns at the time of defining the vector using the "dimnames" attribute.

> matrix_with_names <- matrix(1:6,byrow = TRUE , nrow=2 , dimnames = list(c("Row1","ROw2"),c("Col1","Col2","Col3")))
> matrix_with_names
        Col1 Col2 Col3
Row1     1    2    3
ROw2    4    5    6

Coercion

As I explained in the beginning of this post Matrix is an extension of the Vector. Which means a matrix can hold only a single atomic vector type. But what happens when we try to mix it with different types. That's when coercion happens.

Let's create 2 matrices. Numeric and a character matrices.

> numeric_matrix <- matrix(1:8,ncol = 2)
> numeric_matrix
     [,1] [,2]
[1,]    1    5
[2,]    2    6
[3,]    3    7
[4,]    4    8

> character_matrix <- matrix(LETTERS[1:6], nrow = 4, ncol = 3)
> character_matrix
     [,1] [,2] [,3]
[1,] "A"  "E"  "C"
[2,] "B"  "F"  "D"
[3,] "C"  "A"  "E"
[4,] "D"  "B"  "F"

Now lets try bind these 2 together.
> cbind(numeric_matrix,character_matrix)
      [,1]  [,2]  [,3]  [,4]   [,5]
[1,] "1"  "5"  "A"  "E"  "C"
[2,] "2"  "6"  "B"  "F"  "D"
[3,] "3"  "7"  "C"  "A"  "E"
[4,] "4"  "8"  "D"  "B"  "F"

As you can see the numeric elements were converted to characters.

To have multi dimensional matrices you will need to use lists or frames which I will cover later.

Matrix Sub-setting

Matrix sub setting works the same way as Vectors. However since Matrix is a 2D structure you need  to define rows and columns for sub setting.

> m <- matrix(1:9, byrow =TRUE, nrow = 3)
> m
     [,1] [,2] [,3]
[1,]    1    2    3
[2,]    4    5    6
[3,]    7    8    9

Let's use this matrix of for our examples.

Let's see how we can select 3rd element of the 1st Row.
> m[1,3]
[1] 3

You can just specify the Row and leave the column blank to get all elements of a particular Row.
> m[1,]
[1] 1 2 3

You can just specify the Column and leave the Row blank to get all elements of a particular Column.
> m[,3]
[1] 3 6 9

So what happens if we just set 1 number.
Eg:  > m[4]
[1] 2
It counts the 4th element in the matrix by going through rows first and then columns and gives the output.

So if we say > m[8] you should get 6 as the output.

> m[2,c(2,3)]
[1] 5 6

In this example the output gives the 2nd and 3rd elements of the 2nd row.

> m[c(1,2),c(2,3)]
     [,1] [,2]
[1,]    2    3
[2,]    5    6

This example gives 2nd and 3rd elements of Row 1 and 2.

Note: The same way we used names in vectors we can use the same way to sub set the matrices.
You can use the combination of both index and name.

Logical vectors work the same way. The elements which has True will be selected.

Matrix Arithmetic's

Matrix arithmetic's also work the same way as Vectors. Only difference is you can do an operation using 2 matrices. For an example when you multiply a matrix with another matrix the multiplication will be done element wise. If the number of elements do not match in both matrices Recycling will be performed by R.

Alright that's the basics on Matrices. Well it wont be easy to meet Neo (Keenu Reeves) from the Matrix Film yet.



As you need to practise more to face the Matrix revolution.






Thursday, 11 January 2018

Introduction to R - Part 2 : Vectors

Vectors in R
In this post I will be talking about 3 topics on vectors
  1. Creating and naming vectors
  2. Vector Arithmetic's
  3. Vector Sub-setting
What is a vector


A vector is a sequence of data elements of the same basic data type. Remember the atomic vector types we discussed before? 

Creating and naming vectors
You can create a vector using the c() function. And You can check if a variable is the type of Vector by using is.Vector() function

Try the below commands

vector_poker <- c(12,13,20,21) # Creating a Vector
is.vector(vector_poker) # Checking if the vector_poker is a Vector
is.vector(Area) # Checking if Area is a Vector
class(vector_poker) # Checking the class of the vector_poker



Lets create a Vector with characters.

vector_name <- c("spades" , "hearts" , "diamonds" , "clubs")
So we now have 2 vectors. One with numbers and one with strings.

What if we want to label our numeric Vector with the names in the 2nd vector.
You can simply use the names() function to do this.

names(vector_poker ) <- vector_name 



And now as you can see out vector_poker has names as well as values.

There are different ways to skin a cat. So see the below examples of how to get the same outcome.

vector_option_1 <- c(12,13,20,21) 
vector_name <- c("spades" , "hearts" , "diamonds" , "clubs")
names(vector_option_1) <- vector_name 

vector_option_2 <- c(spades = 12, hearts = 13, diamonds = 20, clubs = 21) 

vector_option_1 <- c("spades" = 12, "hearts" = 13, "diamonds" = 20, "clubs" = 21) 

All 3 options will give you the same output.

Now we know that R vectors have attributes associated with them. When we set the names to the vector we actually set the names attribute of the vector.

You can use the str() function to display the structure of an R object.


Another Important thing to remember is the length() function. Let's try it out.


As the output shows Area and text are vectors with the length 1. And the poker vector shows 4 as the length.

Important
The last important thing is that in R, a vector can only have elements of the same type. They are
also often called "Atomic vectors", to differentiate them from "lists", (List is a data structure
which can hold elements of different data types). This means that you cannot have a vector that
contains both logical data type and numbers, 

If we try to build such a vector, R automatically performs coercion to make sure that you have a vector that contains elements of the same type. Let's see how that works that works

Coercion of Vectors
vector_ranks <- c(9,4,"B" , 11 , "J" , 4, 34 , "R")

In this example there are numeric as well as character elements.

Output 
> vector_ranks
[1] "9"  "4"  "B"  "11" "J"  "4"  "34" "R" 

Now lets check the class of this vector.

> class(vector_ranks)
[1] "character"

Be careful when you have such instances in your code as it could be dangerous. For these kind of scenarios you should probably use a list. We will discuss later how to use a list.


Vector Arithmetic
We already did some arithmetic calculations with Vectors.

Remember Area <- Height * Width

But Let's have a look at Arithmetic calculations for vectors with more than 1 element.

My friends like to play poker on weekends so I thought I will give you an example related to Poker :D. Now don't judge me alright !!!!

Let's say I want to record my gambling earnings for the past 3 days.

earnings <-  c(100, 500 , 200) # Btw this is in US $ 

My good friend Mark likes to play it risk. So he tells me , I will triple your earnings if you manage to beat me in Poker today. Hmmmm I thought about if for a while and I knew he was playing mind games because he had his poker face on. 

But I decided life is too short might as well take a risk LOL. And you know what I won.

So now my total earnings can be calculated like this.

earnings * 3


As you can see it has tripled my earnings for each element.

The same way we can use division , subtraction ,addition and many more can be done. And it will treat the operation for all the elements in the vector.


OK I have to be honest. I did also lose money during the past 3 days.

expenses <- c(50,200,300)
So now I'm gonna calculate my profit.
I can simply say earnings - expenses

> profit <- (earnings*3) - expenses
> profit
[1]  250 1300  300

Last day was terrible I ended up losing 100$ 😐

Now I am going to calculate the sum of my earnings.

> sum(profit)
[1] 1850

Yaaaay I've earned 1850$ not bad eh.
One last thing on Vectors.You can compare 2 vectors using logical operators.

Eg: 
> earnings > expenses
[1]  TRUE  TRUE FALSE

This statement compares each element to see if each earning is greater than the corresponding expense in the expense vector. So now I know the last day I have spent more than what I have earned.

Vector Sub-setting
Subset is selecting parts of an existing vector and creating a new vector.

Subset by Index
You can specify which index you want to output by simply defining the index inside [] (square brackets)

vector_cards
  spades   hearts diamonds    clubs
      11       12       16       13
vector_cards[1]
spades

    11  # The result is a vector with one element

Subset by Name
Instead of using the index you can use the name to specify the element you need.

vector_cards["spades"]
spades
    11

Subset multiple elements
Say you want select spades and clubs then you can simply specify the indices you want to select.
> vector_cards[c(1,4)]
spades  clubs

    11     13

Lets Assign the result to a  new vector called vector_Selection
> vector_selection <- vector_cards[c(1,4)]
> vector_selection
spades  clubs

    11     13

Note : The order depends on the order of the indices
> vector_selection <- vector_cards[c(4,1)]
> vector_selection
 clubs spades

    13     11

You can also use the labels to specify the elements.
> vector_cards[c("clubs","spades")]
 clubs spades

    13     11

Subset all but some
To leave out the first index you can specify the below code.

> vector_cards[-1]
  hearts diamonds    clubs

      12       16       13

You can also remove multiple elements
> vector_cards[-c(1,2)]
diamonds    clubs

      16       13

Note : - operator does not work with names
> vector_cards[-"spades"]

Error in -"spades" : invalid argument to unary operator

Subset using logical vector

The elements with corresponding value TRUE will be kept and FALSE will be removed.

> vector_names[c(TRUE,TRUE,TRUE,FALSE)]
  spades   hearts diamonds

      11       12       16

So what would happen if you specify logical operators less than the total number of elements in the vector. Will that throw an error. Hmmmmmm ....

Well R performs something called recycling..

Lets try using only 2 logical operators.

> vector_names[c(TRUE,FALSE)]
  spades diamonds
      11       16
> vector_names
  spades   hearts diamonds    clubs
      11       12       16       13

As you can see it repeats the contents of the vector until it has the same length

Lets try with 3 logical operators now.

> vector_names[c(TRUE,FALSE,TRUE)]
  spades diamonds    clubs

      11       16       13

Even if we use a vector of length 3 to do the selection , the vector is recycled to end up with a vector of length 4. Which results in appending the first element again.

So behind the scenes the below line of code is executed.

> vector_names[c(TRUE,FALSE,TRUE,TRUE)]
  spades diamonds    clubs

      11       16       13

Woah that's too much for a post isn't it. I think I'm gonna grab a pizza and chillax for the rest of the day.

Next post I will be talking about Matrices.  Stay tuned folks.....




Wednesday, 10 January 2018

Introduction to R - Part 1 : R Basics

Introduction to R



So everyone is talking about R these days and seems to be one of the hot topics right now. So I thought I will share some knowledge on what I have learnt about R. As I mentioned before in one of my previous posts Application Engine has the ability to Call R code for Analytics.

So let's first look at the Basics of R and then see how we can use it in Infor BI.

For the demonstrations I have downloaded the latest R version for Windows from here

And the development tool for R which is R Studio can be downloaded from here.

Installation is pretty simple just go ahead with the defaults and you are good to go.

Below I have opened R Studio in my local environment.



R: The true basics

What is R ? R is the Language for Statistical Computing, 

Developed by Ross Ihaka and Robert Gentleman at the University of Auckland in the 90's. 
It is an open source implementation of the S language, which was developed by John Chambers in the 80's. R provides a wide variety of statistical techniques and visualisation capabilities. Another very important feature about R is that it is highly extensible.

Advantages 
  • Open Source and Free
  • Top notch Graphical capabilities
  • Command Line Interface
  • Reproducible R scripts
  • R Packages available 
  • Community help

Disadvantages 
  • Easy to learn hard to Master
  • Command Line Interface may be daunting at first
  • Poorly written R code may be hard to read and maintain
  • Poorly written code is slow
Ok Let's get started..

On the R studio console lets try the below code

1 + 2 (Hit enter)

You will get the result 3


Variables

You can assign values to variables using <- (Less than and hyphen sign) 

Height <- 5
Width <- 10

You also can do arithmetic operations with the variables.

Eg: Height * Width  will result 50

The list of variables could be checked by using the ls() function.

You can also assign the result of multiplying 2 variables to another variable named Area.

Area <- Height * Width

Now if we run the ls() function it will show Height , Width and Area as available variables.

What happens when we try to access a non existing variable. The console will return an error.


It is important that you clean up your work space when you are done with the code.

For this you can use the function rm()

Eg: removing Height from the work space.

>  rm(Height)

Now if you look at the variables you will only see Area and Width.

> ls()
[1] "Area"  "Width"

Comments
You use the # sign to enter comments. These comments will not be executed by the console.

Basic data types
Data types are also called Atomic Vendor types in R. 

Before we move on with the data types please note the function class(). This is a very useful function to see what type a variable is.

Logical (TRUE | FALSE | NA)


As you can see you can also use T for TRUE and F for FALSE. However it is strongly advised to use the full versions.

Numeric



You will see the difference between the integer 2 and the numeric 2 from the output.

You can use the is. functions to check if the variable is a certain data type.


As you can see integers are numbers but all numbers are not integers

Characters



Other atomic types
double for higher precision numbers
complex for handling complex numbers
raw to store raw bytes. However,


Coercion

Converting one data type to another using the as. function.


Important
TRUE/FALSE is converted to the numeric 1 & 2
Numeric 4 is converted to character "4"
Character "4.5" converted to numeric 4.5

You can even convert this character string, "4.5", to an integer, but this will result in some
information loss, because you cannot keep the decimal part here.

You also need to be aware that not all data conversions are possible. 
Eg: converting "Hello" in to an integer results in NA and a warning message.

Next post I will be talking about Vectors

Wednesday, 3 January 2018

How to Call an Application Engine Process from App Studio

Alright folks in my previous post I gave you an introduction to Application Engine.

We also created out first process and called HelloWorld...

Awesome let's now have a look at calling this process in App Studio.

Let's create a new report and call it App Engine Sample.


This is how my report looks. I thought I will go green :D

I have made the parameter 1 and 2 cells editable so that user can enter the values. You can do this by right clicking on the cell > Format Cells >  Protection Tab > remove the tick on Cells check box

I have also added a button to calculate the values and once calculated it should show the result next to the Answer text box.

Note : I have created a report variable to assign the result from the Application process.

Let's define an action on button.



For the action lets select Application Engine Process..

Select the HelloWorld version. ( I am selecting version 2 as this is my latest version)

Set the rv_Result to Action.Result.Text



Now let's set the input parameters.



Set the 2 cells in your report for Input 1 and 2 . In my case its E4 and E5.

Ok done and dusted. Finally make sure you drag and drop the report variable on to the Answer text box so that it automatically updates when we click on Calculate.

Tadaaaaaaaaaaaaaaaaa


That's just the basics. You can look at the help in App Engine to have a look at the more complex use cases.

Hope this post helped you to get started with App Engine. I am working on more complex scenarios and will keep you posted with my findings soon.







Introduction to Application Engine for Infor BI

I was doing some research on Predictive Analytics recently and there are so many tools in the market which offers this functionality.

Then I thought about Infor BI. Well Infor doesn't have an analytics component inbuilt. At least that what i thought. And then I got to know about Application Engine Process Editor.

So what is Application Engine ??

This is Infor's definition

The Application Engine Process Editor is a tool that you need to define, verify, compile, test, and publish Application Engine processes. In principle, Application Engine processes are plain text files that contain the code written in the BI# programming language. The Application Engine Process Editor includes a text editor and supports the whole life cycle of a process.


Basically you can create custom programs using B# which is very similar to C# and these programs or processes can be called from Application Studio web services.

Let's see how we can do this later in the post.

Let's open up Application Engine and click on the log on button.

Connect to the repository where you will want to store the processes.



Let's go and create a new process...



When you click on the new option it opens up a new process editor shown above.

Copy the below code and paste it to the editor.

#define EngineVersion 4.0
#define RuntimeVersion 4.0

int HelloWorld(int a,int b)
@Description"App Engine Addition";
@Category"Place your process category here";
@Returns"Returns an Integer";
@Parameter[a]: "Input Para 1";
@Parameter[b]: "Input Para 2";
{
    /* To use intellisense press Ctrl-Space and select a function from the list */
    /* To use autocompletion start typing the function. When the right function appears type the round opening bracket and select from a list of available signatures. */
    /* add your BI# code here */

    int number = a + b;
    WriteLocalFile("C:\\Files\\RnD\\Blog\\App Engine\\Test.txt""a+b is : " + number);

    return number;
}

The first 2 lines defines the version of the Engine and the Run time. Use the default 2 lines which you get when you create a new process. The version I am using here might be different to what you have in your environment.

Next you have this line int HelloWorld(int a,int b)

This is the HelloWorld method which takes in 2 integer variables named a,b and outputs an integer variable.

@Description"You need to describe the process here";
@Category"Place your process category here";
@Returns"Define the return type here";
@Parameter[a]: "Input Para 1";
@Parameter[b]: "Input Para 2";

If your method is using input parameters then it is mandatory that you define it using the @Parameter[] function used above. Depending on the number of input variables you have , you need to define @Parameter tags for each of the variables.

Next i add the 2 variables and assign it to a variable named number and I also write the value to a text file in my environment.

    int number = a + b;
    WriteLocalFile("C:\\Files\\Rnd\\Blog\\App Engine\\Test.txt""a+b is : " + number);

    return number;

Finally I return the number variable to the program which called the process.

Now let's test our process............

Before you test it save the definition file so that you do not lose this code :).

Click on the Validate button to make sure the syntax is correct and once confirmed click on the Test Process button.

Enter values for a and b as shown below.....


Click on the run button ( Green button)



There you go, the process add up the 2 numbers and gives an result of 60. And it has also written the value to the Text file.

Let's publish this to the repository so that App studio can call this process.



Once published you can have a look at the processes by clicking on the load button.



Here I have 2 version of my HelloWorld Process. And depending on the details you have specified it will show up here.

In my next post I will explain how to call this process using Application Studio. Stay tuned geeks...


Using Scripting in App studio

I was talking to a very senior Infor BI guy some time back and we were sharing our knowledge on the different tools in the Infor BI Stack.

One of the things he asked me was about Scripting in Infor BI.So today I am sharing with you some of the use cases on Scripting which I have used in my client projects.

Actually there are so many things you can do using scripting in App studio.

It could be from changing a font of a text to hiding rows in the report to forcing a post back in the report. But most of the functionality can be done without using scripting. So today I will talk about couple of requirements which I had to use scripting to cater these requirements.

1.Changing the default text of elements in a dynamic Hyper block
Eg: You might want to change the text of a particular element to show a different name. Lets see how you can achieve this

I have created a VB function named SearchAndReplaceCellValue.



What it does is basically looks for a specific text in the spreadsheet and replaces it with a given text. In this example I am looking for a text "[GL Account].[BS_Cash and cash equivalents].[1]" and replacing this with the text "Cash".

Note [GL Account].[BS_Cash and cash equivalents].[1] is the unique name of the element you want to replace.

There are some additional parameters I provide here.
sColumn - This is the column which will contain the hyperblock text
StartRow - This is the row which the function should start looking for the text
EndRow - This is the rown which the function should end the loop.
EndTag - This is the End of the report
SearchValue - Unique name of the element which you need to change
ReplaceValue - This is the text which will replace the existing value

This is how I call the function
Call SearchAndReplaceCellValue("H", 40, 500, "#END", "[GL Account].[BS_Cash and cash equivalents].[1]", "Cash")

This is the function defintion
Sub SearchAndReplaceCellValue(sColumn, StartRow, EndRow, EndTag, SearchValue, ReplaceValue)

for nRow= StartRow to EndRow
if (GetCellValue(sColumn, nRow)= EndTag) then exit sub

if (GetCellValue(sColumn, nRow)= SearchValue) then
Call SetCellValue(sColumn, nRow, ReplaceValue)
exit sub
end if

next


End Sub

2. Expanding and Collapsing a hyper block using a button click

I have a button which controls the expansion / Collapse functionality. And based on the click I update a variable named ExpandedState to 0 or 1.

Sub btnExpandCollapse_Click ()

if (btnExpandCollapse.Caption = "Collapse") then
Application.SetReportVariable "ExpandState","0"
Call SetDrillDownLevel()
else
Application.SetReportVariable "ExpandState","1"
Call SetDrillDownLevel()
End if


End Sub

Based on the ExpandedState variable value i set the drill down level. Here I have used 1 as the lowest level and 4 as the highest level. You will need to adjust the higher level based on the number of levels in your Hyperblocks.

Sub SetDrillDownLevel()

if (Application.GetReportVariable("ExpandState")="0") then
nLevel=1
else
nLevel=4
End if

Call ExpandHyperBlock("HB1", nLevel, false)
Call ExpandHyperBlock("HB2", nLevel, true)
End Sub

When Expanding/Collapsing multiple Hyperblocks the final call of the expand has the parameter True.. This is to ensure that the report is ONLY updated after all the Hyperblocks are expanded...
'if not the report will be updated at each and every Hyperblock expand/collapse

Sub ExpandHyperBlock(HBName, nLevel, bRecalc)
Dim myDWS
Dim Content
Dim HB

SET DWS=Spreadsheet.DefinitionWorksheet
Set HB=DWS.HyperBlocks.NamedItem(HBName)
HB.DrillDownLevelActive = True
HB.DrillDownLevelStart = nLevel
SET myDWS = nothing

if (bRecalc=True) then Spreadsheet.RecalcReport

End Sub


Using Custom MDX in App Studio

I was working on a fairly simple report for one of the clients I'm doing consulting few days back. At first it looked quite simple. But then suddenly client changed the requirement and I got caught to a Red light. Grrrrrr why do clients always have to change their requirements.

Well .... This is not something new to me , actually this happens quite frequently in the IT world :).

In this instance I had to use a Custom MDX to overcome this problem.


Report Layout

The Site combo drives the work centers in the report layout. The initial requirement was to show the selected Site and all of it's base elements which was a simple structure selection in the hyperblock.

Later they changed the requirement to show All base elements of the selected Site as well as the parent for each base element.

Note : Site in the combo box is in Level 3 where as the base elements can be in different levels in the hierarchy.

So to over come this problem I had to write a custom MDX. What is MDX ??

Multidimensional Expressions (MDX) is a query language for online analytical processing (OLAP) .

It's like using SQL in SQL Server.... But the syntax is different and could be difficult to understand. But with some research on the internet I was able to figure this out.

Below is my MDX

="Generate  (   Descendants (  StrToSet('{"&ReportVariables.rv_Site.Text&"}'), 10,LEAVES  ) ,

                                    UNION ({ [Work Center].CurrentMember.Parent}, Descendants  ( StrToSet('{"&ReportVariables.rv_Site.Text&"}') ,10,LEAVES ) )

                        ) "

Wooooah what the hell is this ???

Ok so let me break this down in to small pieces and explain what I have done here.


I have used the below MDX functions to achieve my goal.

1. Descendants
2. Generate
3. StrtoSet
4. UNION

Let me first explain what these functions do..

Descendants
Returns the set of descendants of a member at a specified level or distance, optionally including or excluding descendants in other levels.

Descendants (  StrToSet('{"&ReportVariables.rv_Site.Text&"}'), 10,LEAVES  )

The report varbale rv_Site contains the selection which the user did. I need to get the base elements of the particular site.  The key word here is "LEAVES" . This means the lowest level members of the hierarchy. The number 10 here means how many levels you need to go down on the hierarchy. Make sure you set a number greater than the lowest level in the hierarchy so that it picks the lowest level members.

StrToSet
Returns the set specified by a Multidimensional Expressions (MDX)– string.

StrToSet('{"&ReportVariables.rv_Site.Text&"}')

What this does is convert the Site value in to a readable MDX format


UNION

Returns a set that is generated by combining two sets.

UNION ({ [Work Center].CurrentMember.Parent}, Descendants  ( StrToSet('{"&ReportVariables.rv_Site.Text&"}') ,10,LEAVES ) )

Here I am combining  [Work Center].CurrentMember.Parent and Descendants  ( StrToSet('{"&ReportVariables.rv_Site.Text&"}') ,10,LEAVES )

What I am actually doing here is getting the parent of each base element and combining it with the base elements of rv_Site.

Generate

Generate function gives you the ability to loop through a set and peform a function on each of the items in the particular set.

So in our example I am selecting all the base elements of rv_site as the Set for the Generate function.

Then i loop through using the [Work Center].CurrentMember.Parent option to get the Parent of each base element.

I hope my explanation makes sense :D. Well if it did not at least I tried lol.

For a for information on MDX you can visit here.

I will post few more MDX related examples in the future. Cheers...


Blog Archive