I revisited a previous post about visualising discrete wavelet transforms because I wanted to remember how I did something. The process is quite old and did not work first time with version 8 of RapidMiner Studio. There have been some subtle changes with respect to the requirements for the type of attributes for the "Join" and "De-Pivot" operators. Never fear, I've updated the process and it's here.
Here is the money shot to prove it still works
An interesting feature of this process is the way it uses the "De-Pivot" operator to transform a matrix-like example set into x,y,z coordinates that can be plotted.
Search this blog
Showing posts with label RegularExpression. Show all posts
Showing posts with label RegularExpression. Show all posts
Wednesday, 24 January 2018
Saturday, 8 March 2014
The Write Special Format operator
Here is a complicated process that does quite a lot...
Deep breath, here we go...
Deep breath, here we go...
- Loop through all the process files in a repository (make sure you point it to your location) and read each one in as a document.
- Convert the documents to normal examples within a single example set.
- Create an attribute called `description` that contains the text within the top-level comment for each process. This uses Ninja XPath (actually it doesn't but I wanted to use the word Ninja).
- Do gymnastics to reformat the contents of the attribute. This uses Ninja regular expressions (actually it doesn't but the usual rule is all regular expressions require Ninja like skills). Newlines and linefeeds are included - this is perhaps the interesting part.
- Rename to make the name of the attribute easier to understand.
- Select only the attributes of interest.
- Filter out all where there is no description.
- Write the example set to a file (make sure you set this to somewhere you want to write files). This uses the `Write Special Format` operator which was the only way I could find to get this to work.
Why?
The comment view can contain html. This allows basic formatting and structure to be defined so that when a process is opened, the text in the comment view is displayed with this formatting and structure applied. The process above allows the html tags to be extracted so they can be re-used if you want to use the formatting and structure somewhere else like a document or index. Time is precious, anything that allows re-use and avoids typing is good.
The `Write Special Format` operator is especially nice because it allows precise control to be exerted over what is to be written. Dare I say Ninja like control?
Saturday, 5 October 2013
Parsing macros with values containing backslashes
Let's imagine we are looping through some files and we happen to know that the files are buried somewhere in folders called chapterNN where NN is a number. How can we extract information about the location for each file individually?
Within a Loop Files operator, the parent_path macro would take values like this within the loop (I'm assuming Windows obviously).
c:\myGiantFolderStructure\mybook\chapter01
c:\myGiantFolderStructure\mybook\chapter02
...
c:\myGiantFolderStructure\mybook\chapter11
If we wanted to extract the last part of the folder name including the number to use in the loop we could try the Generate Macro operator with the replaceAll function.
The basic idea would be to configure the operator like so.
chapter = replaceAll("%{parent_path}", ".*(chapter.*)", "$1")
This matches the entire macro with a regular expression but locates the word "chapter" and whatever follows it inside a capturing group. This replaces the entire value of the parent_path macro. The result should be a new macro called chapter with values from chapter01 upwards.
Unfortunately, this doesn't work because the backslashes cause trouble. I'm guessing but I think the replaceAll function (and its siblings, replace and concat) try to parse the string within the macro and get confused by treating the backslash as an escape character.
Fortunately, there is a solution: a little known function called macro. This function simply returns the string representation of a named macro.
The expression would then look like this.
chapter = replaceAll(macro("parent_path"), ".*(chapter.*)", "$1")
Knowing that backslashes are processed enables to us work out how to pass them simply by escaping them. If we felt we needed a more sophisticated match inside the capturing group to ensure we picked up numbers, we would do the following.
chapter = replaceAll(macro("parent_path"), ".*(chapter\\d+)", "$1")
This matches only if there is at least one number after the word "chapter". The double backslash becomes a single backslash when the regular expression is evaluated and \d+ means one or more numbers.
Within a Loop Files operator, the parent_path macro would take values like this within the loop (I'm assuming Windows obviously).
c:\myGiantFolderStructure\mybook\chapter01
c:\myGiantFolderStructure\mybook\chapter02
...
c:\myGiantFolderStructure\mybook\chapter11
The basic idea would be to configure the operator like so.
chapter = replaceAll("%{parent_path}", ".*(chapter.*)", "$1")
This matches the entire macro with a regular expression but locates the word "chapter" and whatever follows it inside a capturing group. This replaces the entire value of the parent_path macro. The result should be a new macro called chapter with values from chapter01 upwards.
Unfortunately, this doesn't work because the backslashes cause trouble. I'm guessing but I think the replaceAll function (and its siblings, replace and concat) try to parse the string within the macro and get confused by treating the backslash as an escape character.
Fortunately, there is a solution: a little known function called macro. This function simply returns the string representation of a named macro.
The expression would then look like this.
chapter = replaceAll(macro("parent_path"), ".*(chapter.*)", "$1")
Knowing that backslashes are processed enables to us work out how to pass them simply by escaping them. If we felt we needed a more sophisticated match inside the capturing group to ensure we picked up numbers, we would do the following.
chapter = replaceAll(macro("parent_path"), ".*(chapter\\d+)", "$1")
This matches only if there is at least one number after the word "chapter". The double backslash becomes a single backslash when the regular expression is evaluated and \d+ means one or more numbers.
Wednesday, 13 February 2013
Sorting discretized examples in the order nature intended
Using the "Discretize" operators puts examples into different nominal bins depending on the value of an attribute. When using the long form of the name type, the possible nominal value names are of this general format "rangeN [x-y]"
N starts at 1 and ends at whatever the largest bin number is. The N is not preceded by any leading zeros so this means that when sorted, range10 comes before range2. When using the histogram plotter this is OK because the nominal values have an implicit order that gets used. When using the advanced plotter however, a histogram comes out wrong.
Here's a histogram, produced using the advanced plotter, showing the original ordering. The data is 10,000 examples generated by multiplying 5 random numbers together and normalizing to the range 0 to 1.
As can be seen, the ordering of the bins is not in the same numerical order of the underlying numerical values.
This can be fixed by using regular expressions and the "Replace" operator.
I'm not enough of a regular expression ninja to do this in one operator so I had to use two.
So, in the first "Replace" operator, set the "replace what" field to
In the second "Replace" operator, set the replace field to
Be aware that you might have to tweak these numbers if the number of names is different in your case.
The end result is then a histogram like this
Now the ordering is the same as the implied numerical ordering.
N starts at 1 and ends at whatever the largest bin number is. The N is not preceded by any leading zeros so this means that when sorted, range10 comes before range2. When using the histogram plotter this is OK because the nominal values have an implicit order that gets used. When using the advanced plotter however, a histogram comes out wrong.
Here's a histogram, produced using the advanced plotter, showing the original ordering. The data is 10,000 examples generated by multiplying 5 random numbers together and normalizing to the range 0 to 1.
As can be seen, the ordering of the bins is not in the same numerical order of the underlying numerical values.
This can be fixed by using regular expressions and the "Replace" operator.
I'm not enough of a regular expression ninja to do this in one operator so I had to use two.
So, in the first "Replace" operator, set the "replace what" field to
range(\d+.*)and set the replace by field to
range0000$1This will change all the values to have leading zeros inserted before the number within the value.
In the second "Replace" operator, set the replace field to
range0+(\d{4})(.*)and set the replace by field to
range$1$2This ensures that all the numeric parts of the range name are of the same length and are preceded by at least one leading 0.
Be aware that you might have to tweak these numbers if the number of names is different in your case.
The end result is then a histogram like this
Now the ordering is the same as the implied numerical ordering.
Labels:
AdvancedPlotter,
Discretize,
RegularExpression,
Replace
Wednesday, 1 August 2012
Deleting attributes with 2 valid values and the rest missing
For some reason the other day, I can't remember why, I had to delete attributes with two valid values and all the rest missing. It followed on from this process.
So I made this process that finds all attributes with a specific number of valid values and removes them (the attributes).
This example uses sample data so deletes attributes with 7 valid values but I'm sure you'll get the idea.
So I made this process that finds all attributes with a specific number of valid values and removes them (the attributes).
This example uses sample data so deletes attributes with 7 valid values but I'm sure you'll get the idea.
Labels:
ExtractMacro,
GenerateMacro,
RegularExpression,
Rename,
SelectAttributes
Tuesday, 25 October 2011
Counting words in a document
Here's an example that counts the total number of words in a document followed by the total number of unique words.
It does this by using the "cut document" operator with the following regular expression.
This splits the document into words and each word is returned as a document based on the result of the capturing group; the brackets define the capturing group; everything inside these is returned as a value. The "Documents To Data" operator converts all the documents, one for each word, into examples in an example set. The text field name is set to "word" and this is used in the later operators.
An "Extract Macro" operator obtains the number of examples. This is the same as the count of all words. An aggregation is performed to count words and another "Extract Macro" operator determines the number of unique words from the resulting example set. These macros are reported as log values using the "Provide Macro As Log Value" operators and the log file is converted to an example set using "Log To Data".
Other regular expressions could be used if you want to ignore numbers and inside the "Cut Document" operator it is possible to have other filtering operators such as stemming.
It does this by using the "cut document" operator with the following regular expression.
(\w+)
This splits the document into words and each word is returned as a document based on the result of the capturing group; the brackets define the capturing group; everything inside these is returned as a value. The "Documents To Data" operator converts all the documents, one for each word, into examples in an example set. The text field name is set to "word" and this is used in the later operators.
An "Extract Macro" operator obtains the number of examples. This is the same as the count of all words. An aggregation is performed to count words and another "Extract Macro" operator determines the number of unique words from the resulting example set. These macros are reported as log values using the "Provide Macro As Log Value" operators and the log file is converted to an example set using "Log To Data".
Other regular expressions could be used if you want to ignore numbers and inside the "Cut Document" operator it is possible to have other filtering operators such as stemming.
Saturday, 17 September 2011
Regular expressions: negative look behind
I'm pleased to report after much gymastics that the regular expression feature "regular look behind" works with the "Replace" operator.
So if you have an attribute containing
"mit seit nach bei gegenuber von zu aus"
and you want to replace everything except the word "von" with "bier" then the following regular expression will do it.
replace what - \w+\b(?<!\bvon)
replace by - bier
The result is
"bier bier bier bier bier von bier bier"
Prost! :)
So if you have an attribute containing
"mit seit nach bei gegenuber von zu aus"
and you want to replace everything except the word "von" with "bier" then the following regular expression will do it.
replace what - \w+\b(?<!\bvon)
replace by - bier
The result is
"bier bier bier bier bier von bier bier"
Prost! :)
Monday, 15 August 2011
Visualizing discrete wavelet transforms: part II
Here is a process that takes the discrete wavelet transform (it happens to be the Daubechies 4 wavelet in this case rather than the Haar but the results are similar) of some fake data and plots the corresponding results. This is different from the maximum overlap discrete wavelet transform from the previous post.
The result looks like this.
(the z2 attribute is plotted as the colour using log scaling)
The bottom row is the clean signal (with scaling to make it show up), the 2nd row from the bottom is the noisy signal and the third row from the bottom is the result of the discrete wavelet transform. This is only included for completeness since it does not correspond in the original domain to the signal. For an interpretation of this I found the following in the code of the discrete wavelet transform (in file DiscreteWaveletTransformation.java)
In the case of the "normal" DWT the output value series has just one dimension, and these
coefficients are to be interpreted as follows: the first N/2 coefficients are the wavelet
coefficients of scale 1, the following N/4 coefficients of scale 2, the next N/8 of scale
4 etc. (dyadic subsampling of both the time and scale dimension). The last remaining
coefficient is the last scaling coefficient.
So this means the 4th row from the bottom corresponds to the coefficients of scale 1, the 5th row to scale 2 and so on. The coefficients have been replicated as many times as required to match the x scale.
Note that this differs from the view tradionally presented in the literature where the high frequencies are presented at the top. That's an exercise for another day.
The main difference between this and the previous MODWT example is the unpacking of the DWT result. For this I used a Groovy script. This takes 2 example sets as input and copies the correct parts of the first (the DWT result) into the second (the output that will eventually be de-pivoted).
The output example set that is fed into the Groovy script is created using a "Generate Data" operator since I found this to be the easiest way to generate the example set with the right number of rows and columns.
As before, the graphic shows that the algorithm has seen the presence of the low frequency signal at the expected location from x = 5000 and there is perhaps a hint that something has been spotted at x = 100.
The result looks like this.
(the z2 attribute is plotted as the colour using log scaling)
The bottom row is the clean signal (with scaling to make it show up), the 2nd row from the bottom is the noisy signal and the third row from the bottom is the result of the discrete wavelet transform. This is only included for completeness since it does not correspond in the original domain to the signal. For an interpretation of this I found the following in the code of the discrete wavelet transform (in file DiscreteWaveletTransformation.java)
In the case of the "normal" DWT the output value series has just one dimension, and these
coefficients are to be interpreted as follows: the first N/2 coefficients are the wavelet
coefficients of scale 1, the following N/4 coefficients of scale 2, the next N/8 of scale
4 etc. (dyadic subsampling of both the time and scale dimension). The last remaining
coefficient is the last scaling coefficient.
So this means the 4th row from the bottom corresponds to the coefficients of scale 1, the 5th row to scale 2 and so on. The coefficients have been replicated as many times as required to match the x scale.
Note that this differs from the view tradionally presented in the literature where the high frequencies are presented at the top. That's an exercise for another day.
The main difference between this and the previous MODWT example is the unpacking of the DWT result. For this I used a Groovy script. This takes 2 example sets as input and copies the correct parts of the first (the DWT result) into the second (the output that will eventually be de-pivoted).
The output example set that is fed into the Groovy script is created using a "Generate Data" operator since I found this to be the easiest way to generate the example set with the right number of rows and columns.
As before, the graphic shows that the algorithm has seen the presence of the low frequency signal at the expected location from x = 5000 and there is perhaps a hint that something has been spotted at x = 100.
Labels:
De-Pivot,
Groovy,
RegularExpression,
Valueseries,
Wavelets
Thursday, 11 August 2011
Visualizing discrete wavelet transforms
RapidMiner can transform data using wavelet transforms within the value series extension. As part of my endeavour to learn about these I made a process that allows visualisation of the results of a MODWT transform. It's intended to show at a glance what the transformation has done to the data.
Amongst others, it uses the "data to series", "series to data" and "de-pivot" operators and of course the "discrete wavelet transform".
The process creates some fake data consisting of 8192 records. A high frequency square wave is located from position 100 to 600 and a lower frequency wave is located from 5000 to 5500. A significant amount of noise is also added to hide the signal.
If you plot the results of the de-pivot operation and use the block plotter, choose x, y and z2 and set the z2 axis to a log scale, you should see something like this.
The bottom row corresponds to the pure signal (note its amplitude has been scaled to make it show up better), the next row up the noisy signal and all the rows above that correspond to the different output resolutions of the MODWT transform. The top row is the average for all the signals and should be 0 owing to the normalisations performed on the input data. All of this is produced from the MODWT output using the de-pivot operator after a certain amount of joining gymnastics.
The plot shows that the transform has detected a match from the 5000 point for the original signal. The signal from 100 is not so obvious.
The individual outputs from the MODWT operation are also available. Here for example is a plot of the 6th output (i.e. the 8th row in the graphic above).
Compare this with the raw noisy data.
Clearly there is something in the data and the transform is able to isolate this to a certain extent.
My next process will be one to visualise the DWT rather than the MODWT output.
Amongst others, it uses the "data to series", "series to data" and "de-pivot" operators and of course the "discrete wavelet transform".
The process creates some fake data consisting of 8192 records. A high frequency square wave is located from position 100 to 600 and a lower frequency wave is located from 5000 to 5500. A significant amount of noise is also added to hide the signal.
If you plot the results of the de-pivot operation and use the block plotter, choose x, y and z2 and set the z2 axis to a log scale, you should see something like this.
The bottom row corresponds to the pure signal (note its amplitude has been scaled to make it show up better), the next row up the noisy signal and all the rows above that correspond to the different output resolutions of the MODWT transform. The top row is the average for all the signals and should be 0 owing to the normalisations performed on the input data. All of this is produced from the MODWT output using the de-pivot operator after a certain amount of joining gymnastics.
The plot shows that the transform has detected a match from the 5000 point for the original signal. The signal from 100 is not so obvious.
The individual outputs from the MODWT operation are also available. Here for example is a plot of the 6th output (i.e. the 8th row in the graphic above).
Compare this with the raw noisy data.
Clearly there is something in the data and the transform is able to isolate this to a certain extent.
My next process will be one to visualise the DWT rather than the MODWT output.
Labels:
De-Pivot,
RegularExpression,
Valueseries,
Wavelets
Sunday, 7 August 2011
Finding processes in the repository
I have a lot of processes and it's sometimes hard to remember the name of one containing something useful. A metadata view and search capability of the repository would be very useful.
While that is being invented, here's a process that scans through a repository and determines the name and location of the processes it finds. It also counts the number of operators used to give a sense of how big the process is. The process recursively scans files with extension ".rmp" from a starting folder. Various document operators including "extract information" and "cut documents" using Xpath are used to process the files and extract information from them. The "aggregate" operator is used to count operators.
One day I will make it into a report that can be viewed using RapidAnalytics.
Other data is also extracted and this could be used if desired.
The default location is the default Windows location of the samples processes; change this to the folder where your repository is.
Some gymnastics were required to get the loop files operator to work correctly. This seems to present zero sized directories when iterating so I was forced to use the "branch" operator to eliminate these. Pointing the loop files operator to the C:\ drive usually results in an error that I haven't got to the bottom of yet.
While that is being invented, here's a process that scans through a repository and determines the name and location of the processes it finds. It also counts the number of operators used to give a sense of how big the process is. The process recursively scans files with extension ".rmp" from a starting folder. Various document operators including "extract information" and "cut documents" using Xpath are used to process the files and extract information from them. The "aggregate" operator is used to count operators.
One day I will make it into a report that can be viewed using RapidAnalytics.
Other data is also extracted and this could be used if desired.
The default location is the default Windows location of the samples processes; change this to the folder where your repository is.
Some gymnastics were required to get the loop files operator to work correctly. This seems to present zero sized directories when iterating so I was forced to use the "branch" operator to eliminate these. Pointing the loop files operator to the C:\ drive usually results in an error that I haven't got to the bottom of yet.
Thursday, 7 July 2011
Using regular expressions with the Replace (Dictionary) operator
The "Replace (Dictionary)" operator replaces occurrences of one nominal in one example set with another looked up from another example set.
By default, this replaces a continuous sequence regardless of its position in the nominal.
For example, if an attribute in the main example set contains the value "network" and the dictionary example set contains the value pair "work", "banana", the result of the operation would be "netbanana".
This is fine but if you want to limit to whole words only then you can use the "use regular expressions" parameter in the replace operator. To make this work, you also have to change the text within the dictionary for the nominal to be replaced with "\b" at the beginning and the end. In regular expression speak, this means match a whole word only.
In addition, if the word to be replaced contains reserved characters (from a regular expressions perspective) then "\Q" and "\E" have to be placed around the word.
One way to do this is to use a "generate attributes" operator and create a new attribute in the dictionary example set using the following expression.
The new attribute would then be used in the "from attribute" parameter of the "Replace (Dictionary)" operator. The "to attribute" would be set to the attribute within the example set dictionary that is the replacement value.
By default, this replaces a continuous sequence regardless of its position in the nominal.
For example, if an attribute in the main example set contains the value "network" and the dictionary example set contains the value pair "work", "banana", the result of the operation would be "netbanana".
This is fine but if you want to limit to whole words only then you can use the "use regular expressions" parameter in the replace operator. To make this work, you also have to change the text within the dictionary for the nominal to be replaced with "\b" at the beginning and the end. In regular expression speak, this means match a whole word only.
In addition, if the word to be replaced contains reserved characters (from a regular expressions perspective) then "\Q" and "\E" have to be placed around the word.
One way to do this is to use a "generate attributes" operator and create a new attribute in the dictionary example set using the following expression.
"\\b\\Q"+word+"\\E\\b"In this case, "word" is the attribute containing the word to be replaced. The "\" must be escaped with an additional "\" in order for it all to come out correctly.
The new attribute would then be used in the "from attribute" parameter of the "Replace (Dictionary)" operator. The "to attribute" would be set to the attribute within the example set dictionary that is the replacement value.
Wednesday, 30 March 2011
Renaming attributes with regular expressions
If you have an attribute with this name
Firstpart_Secondpart_Thirdpart
and you want to rename it to
Thirdpart_Secondpart_Firstpart
Use the 'Rename By Replacing' operator with this
(.*)_(.*)_(.*)
in the 'replace what' field
and
$3_$2_$1
in the 'replace by' field.
The brackets denote what is known as a capturing group so that everything inside that matches can be used later on by the use of the $1, $2 and $3 entries. The '.*' means match 0 or more characters and will continue until the '_' is found. The capturing group brackets mean that everything from the beginning of the string up to the character before the '_' will be placed into capturing group 1.
Firstpart_Secondpart_Thirdpart
and you want to rename it to
Thirdpart_Secondpart_Firstpart
Use the 'Rename By Replacing' operator with this
(.*)_(.*)_(.*)
in the 'replace what' field
and
$3_$2_$1
in the 'replace by' field.
The brackets denote what is known as a capturing group so that everything inside that matches can be used later on by the use of the $1, $2 and $3 entries. The '.*' means match 0 or more characters and will continue until the '_' is found. The capturing group brackets mean that everything from the beginning of the string up to the character before the '_' will be placed into capturing group 1.
Subscribe to:
Posts (Atom)







