LAE

Welcome to the LAE community!  Please feel free to start a discussion in the discussion tab or join in a conversation.

Discussions

Members

Resources

Events

 View Only

Python Node - 2D Array

  • 1.  Python Node - 2D Array

    Employee
    Posted 03-28-2017 12:09

    Note: This was originally posted by an inactive account. Content was preserved by moving under an admin account.

    Originally posted by: jpstory

    Hi All

    I need to load the input data into a 2D array for some later manipulation/calculation.

    I was able to create the array and load the data from the input pin into the array.However the process somehow terminates itself once the array finshes data-loading.

    This prevents me from performing further actions on the data stored in the array.

    Specifically, codes outside of "while quant.permitsRunning(self):" seemed to be ignored. I tried using While... else.... statement but got no luck.

    I have attached my sample code, thanks in advane for your hlep!

    LAE_Python_2DArray_Operation.txt


  • 2.  RE: Python Node - 2D Array

    Employee
    Posted 03-28-2017 13:17

    Note: This was originally posted by an inactive account. Content was preserved by moving under an admin account.

    Originally posted by: stonysmith

    Try the code below. I don't understand what the "else" on your line 59 was supposed to do.
    I've never heard of while/else in python.
    It looks to me like the whole "else" clause was being skipped.


    import braininfo
    
    def setup(brainNodeControlObj, BrainNodeClass):
    	class BrainNode(BrainNodeClass):
    		def initialize(self):
    			super(BrainNode, self).initialize()
    
    			metadata = self.newMetadata()
    			metadata.append("Field1","String")
    			metadata.append("Field2","String")
    			metadata.append("Field3","String")
    			metadata.append("Field4","String")
    			metadata.append("Field5","String")
    			self.outputs[0].metadata = metadata
    			#Create an empty array
    			self.Column = 5
    			self.Row = 2
    			self.Tbl = [["x" for C in range(self.Column)] for R in range(self.Row)]
    						
    		def finalize(self, val):
    			rLoop = -1
    			for a in range(self.Row):
    				outRec = self.outputs[0].newRecord()
    				rLoop = rLoop + 1
    				cLoop = -1
    				for b in range(self.Column):
    					cLoop = cLoop + 1
    					#outRec[cLoop] = cLoop
    					#outRec[cLoop] = "z" 
    					outRec[cLoop] = self.Tbl[rLoop][cLoop]
    				self.outputs[0].write(outRec)
    			super(BrainNode, self).finalize(val)
    
    		def pump(self, quant):	
    			#Load input data into array
    			rLoop = -1
    			while quant.permitsRunning(self):
    				inRec = self.inputs[0].read()
    				if not inRec:
    					return False
    				rLoop = rLoop + 1
    				cLoop = -1
    				for field in inRec:
    					cLoop = cLoop + 1
    					self.Tbl[rLoop][cLoop] = field
    			#Iterate through array and populate into the output table	
    	return BrainNode


  • 3.  RE: Python Node - 2D Array

    Employee
    Posted 03-28-2017 13:37

    Note: This was originally posted by an inactive account. Content was preserved by moving under an admin account.

    Originally posted by: jpstory

    Hi Stony

    Thanks for the quick turnaround, this is really helpful.

    To anser your question, the "else" was simply a shot in the dark.

    Also, it's interesting about def finalize as I have been wondering what exactly it is there for since day one.

    To clarify, so it exists to handle any post-looping (looping as loop through each record of the input data) operations ?


  • 4.  RE: Python Node - 2D Array

    Employee
    Posted 03-28-2017 14:03

    Note: This was originally posted by an inactive account. Content was preserved by moving under an admin account.

    Originally posted by: stonysmith

    The sequence is

    initialize
    pump
    finalize


    I often don't do it the way you did it ... I rarely use finalize, but I left it your way because it builds the entire array before it writes it out.
    You need to be careful that you don't fill up memory with the array you are building.


  • 5.  RE: Python Node - 2D Array

    Employee
    Posted 03-29-2017 06:32

    Note: This was originally posted by an inactive account. Content was preserved by moving under an admin account.

    Originally posted by: jpstory

    If not too much trouble could you please show me how would you do it, instead of my way.

    Thanks for the advice, won't use it on massive data set.


  • 6.  RE: Python Node - 2D Array

    Employee
    Posted 03-29-2017 06:53

    Note: This was originally posted by an inactive account. Content was preserved by moving under an admin account.

    Originally posted by: stonysmith

    Originally posted by: jpstory
    					

    If not too much trouble could you please show me how would you do it, instead of my way.
    I would have to know more about what you are attempting to do. If you are "only" doing the transformation in the current code, then a simple ChangeMetadata node will accomplish what you want. If you are trying to apply some additional transformation that you've not shown.. I'd have to understand what you're after.

    But, if we stick only to what you were doing, then you can simply output each record as you read it.. it'll process an infinite number of records with no problem.

    import braininfo
    
    def setup(brainNodeControlObj, BrainNodeClass):
    	class BrainNode(BrainNodeClass):
    		def initialize(self):
    			super(BrainNode, self).initialize()
    
    			metadata = self.newMetadata()
    			metadata.append("Field1","String")
    			metadata.append("Field2","String")
    			metadata.append("Field3","String")
    			metadata.append("Field4","String")
    			metadata.append("Field5","String")
    			self.outputs[0].metadata = metadata
    			
    		def finalize(self, val):
    			super(BrainNode, self).finalize(val)
    
    		def pump(self, quant):	
    			while quant.permitsRunning(self):
    				inRec = self.inputs[0].read()
    				outRec = self.outputs[0].newRecord()
    				if not inRec:
    					return False
    				for col in range(len(inRec)):
    					outRec[col] = inRec[col]
    				self.outputs[0].write(outRec)
    	return BrainNode


  • 7.  RE: Python Node - 2D Array

    Employee
    Posted 03-29-2017 07:01

    Note: This was originally posted by an inactive account. Content was preserved by moving under an admin account.

    Originally posted by: stonysmith

    On a separate topic.... (unrelated to the above) ... for future reference:

    Both of these assignments will work:

    fieldnameIn="abc"  #character name
    fieldnameOut="def"  #character name
    outRec[fieldnameOut] = inRec[fieldnameIn]
    fieldnumberIn=1   #integer
    fieldnumberOut=21   #integer
    outRec[fieldnumberOut] = inRec[fieldnumberIn]
    But, indexing by name takes a significant amount more processing than indexing by number and therefore is MUCH slower.


  • 8.  RE: Python Node - 2D Array

    Employee
    Posted 03-29-2017 11:12

    Note: This was originally posted by an inactive account. Content was preserved by moving under an admin account.

    Originally posted by: jpstory

    Brilliant!

    One thing I am trying to achive with the 2D array is to do a Table Rotation. Is it achievable ?

    for example the input is a table of:
    Name, City
    Adam, AAA
    Tame, BBB
    Dame, CCC

    After rotation, it should be something like:
    FieldName, Record1, Record2, Record3
    Name, Adam Tame Dame
    City, AAA BBB CCC


  • 9.  RE: Python Node - 2D Array

    Employee
    Posted 03-29-2017 11:25

    Note: This was originally posted by an inactive account. Content was preserved by moving under an admin account.

    Originally posted by: stonysmith

    We can certainly go the python route, but try this first without using Python...

    node:Static_Data_2
    bretype:core::Static Data
    editor:sortkey=58d532be43b56c18
    output:@40fe6c55598828e5/=
    prop:StaticData=<<EOX
    color:string, id:int,type:string,rand:int,junk:string
    Red,1,primary,27959,okay
    Green,2,secondary,27960,no
    EOX
    editor:XY=650,290
    end:Static_Data_2
    
    node:Add_Row_Number
    bretype:core::Filter
    editor:Label=Add Row Number
    editor:sortkey=58dbfae05012449d
    input:@40fd2c74167f1ca2/=Static_Data_2.40fe6c55598828e5
    output:@40fd2c7420761db6/=
    prop:Script=<<EOX
    
    emit *
    emit "Record_"+execCount.str() as _RowNum
    EOX
    editor:XY=620,370
    end:Add_Row_Number
    
    node:Pivot__Names_To_Data
    bretype:core::Pivot - Names To Data
    editor:sortkey=58dbfa7c08dc094e
    input:@4c8fdfd712226f21/=Add_Row_Number.40fd2c7420761db6
    output:@4c93bf5c2ec21c56/=
    prop:DataOutputField=Value
    prop:NameOutputField=FieldName
    prop:PivotFields=color,id,junk,rand,type
    prop:TypeConversion=To String
    editor:XY=710,370
    end:Pivot__Names_To_Data
    
    node:Pivot__Data_To_Names
    bretype:core::Pivot - Data To Names
    editor:sortkey=58dbfa7e78cb3b77
    input:@4ca9ea0e77f858a8/=Pivot__Names_To_Data.4c93bf5c2ec21c56
    output:@4ca9ea197eed4a1a/=
    prop:DataField=Value
    prop:GroupBy=<<EOX
    FieldName
    EOX
    prop:NamesField=_RowNum
    editor:XY=820,370
    node:GroupByPath
    bretype:::GroupByPath
    editor:shadow=4cd2e31a25825f1b
    input:@4ccfdae429e85cb5/=
    output:@4ccfdae4585258de/=
    node:DataToNamesWithGroupBy
    bretype:::DataToNamesWithGroupBy
    editor:shadow=4cd2e31a003467e9
    input:@4cceefe2650d45e1/=
    input:@4cceefe45c596005/=
    output:@4cceeff7142d3549/=
    end:DataToNamesWithGroupBy
    
    node:Sort
    bretype:::Sort
    editor:shadow=4cd2e31a71bb6ba4
    input:@40fd2c743ebf4304/=
    output:@40fd2c746a2a3b47/=
    end:Sort
    
    node:Agg
    bretype:::Agg
    editor:shadow=4cd2e31a69415a14
    input:@40fd2c7427456e5b/=
    output:@40fd2c744c862db0/=
    output:@4cd003f34b1437c4/=
    end:Agg
    
    node:Bypass
    bretype:::Bypass
    editor:shadow=4cd2e31a078b0885
    input:@4b467f7e129d45c1/=
    input:@4b467f830ffe047b/=
    output:@40fd2c7436717256/=
    end:Bypass
    
    node:Agg_2
    bretype:::Agg
    editor:shadow=4cd2e31a0deb376a
    input:@40fd2c7427456e5b/=
    output:@40fd2c744c862db0/=
    end:Agg_2
    
    end:GroupByPath
    
    node:NoGroupByPath
    bretype:::NoGroupByPath
    editor:shadow=4cd2e31a55c8124a
    input:@4ccfdae429e85cb5/=
    output:@4ccfdae4585258de/=
    node:DataToNamesNoGroupBy
    bretype:::DataToNamesNoGroupBy
    editor:shadow=4cd2e31a1d6263ef
    input:@4cceefe2650d45e1/=
    input:@4cceefe45c596005/=
    output:@4cceeff7142d3549/=
    end:DataToNamesNoGroupBy
    
    node:Agg
    bretype:::Agg
    editor:shadow=4cd2e31a1bbb492b
    input:@40fd2c7427456e5b/=
    output:@40fd2c744c862db0/=
    end:Agg
    
    end:NoGroupByPath
    
    node:Bypass
    bretype:::Bypass
    editor:shadow=4cd2e31a1cd03eb3
    input:@4ccfdb241245012a/=
    input:@4ccfdb25174c47e7/=
    output:@40fd2c7436717256/=
    end:Bypass
    
    end:Pivot__Data_To_Names
    ** again, don't use this on huge datasets <grin>
    But it would work with datasets much larger than the python method.


  • 10.  RE: Python Node - 2D Array

    Employee
    Posted 03-29-2017 12:53

    Note: This was originally posted by an inactive account. Content was preserved by moving under an admin account.

    Originally posted by: jpstory

    This is awesome!

    A minor issue is that except for _RowNum all the rest field names need to be input into Pivot - Names To Data node.

    The files I am dealing with between 1500 and 2000 fields ( I know, it's crazy), it'll be difficult to keep inputing the field names for every different report.

    That is why Python come up as an option, hope you understand.


  • 11.  RE: Python Node - 2D Array

    Employee
    Posted 03-30-2017 06:50

    Note: This was originally posted by an inactive account. Content was preserved by moving under an admin account.

    Originally posted by: awilliams1024

    An alternative approach would be to use the R language transform operator within either the Power R node (if licensed) or the R Node.
    Note that R uses in-memory processing so you need to ensure your machine has sufficient available RAM to load and transform the data.

    Here is an example using the Power R node:

    node:Static_Data
    bretype:core::Static Data
    editor:sortkey=58d532be43b56c18_2
    output:@40fe6c55598828e5/=
    prop:StaticData=<<EOX
    color:string, id:int,type:string,rand:int,junk:string
    Red,1,primary,27959,okay
    Green,2,secondary,27960,no
    EOX
    editor:XY=340,210
    end:Static_Data
    
    node:Power_R__Transpose_DF
    bretype:lal1::Power R
    editor:Label=Power R - Transpose DF
    editor:sortkey=58dd008f22552dfb
    input:58dd0094043a07d2/in1=Static_Data.40fe6c55598828e5
    output:58dd0095543d6ad7/out1=
    prop:RScript=<<EOX
    
    ## Get the number of records in the input data frame
    NumRec <- nrow(in1)
    
    ## Get the variable names from the input data frame
    FieldName <- colnames(in1)
    
    ## Transpose the input data set
    tr <- t(in1)
    
    ## Delete the original data frame
    rm(in1)
    
    #### Build the list of variable names
    ## Set the first variable name in the name vector
    Prefix <- c("Record_")
    varNames <- paste0(Prefix,"1")
    ## Append subsequent variable names
    i = 1
    if (NumRec > 1)
    while (i < NumRec) {
    	i <- i + 1
    	varNames <- c(varNames, paste0(Prefix, as.character(i)))
    }
    
    ## Set the column name attributes on the transformed data frame
    colnames(tr) <- varNames
    
    ## Output the data
    out1 <- data.frame(FieldName, tr)
    EOX
    editor:XY=460,210
    editor:Doc=Transposes an Data Frame
    end:Power_R__Transpose_DF
    The corresponding R node example is:

    node:R__Transpose_DF
    bretype:r::R
    editor:Label=R - Transpose DF
    editor:sortkey=58dd082b64316ad2
    input:58dd082e478c4cf9/in1=Static_Data_3.40fe6c55598828e5
    output:58dd082f61950e0c/out1=
    prop:RScript=<<EOX
    
    ## Get the number of records in the input data frame
    NumRec <- nrow(in1)
    
    ## Get the variable names from the input data frame
    FieldName <- colnames(in1)
    
    ## Transpose the input data set
    tr <- t(in1)
    
    ## Delete the original data frame
    rm(in1)
    
    #### Build the list of variable names
    ## Set the first variable name in the name vector
    Prefix <- c("Record_")
    varNames <- paste0(Prefix,"1")
    ## Append subsequent variable names
    i = 1
    if (NumRec > 1)
    while (i < NumRec) {
    	i <- i + 1
    	varNames <- c(varNames, paste0(Prefix, as.character(i)))
    }
    
    ## Set the column name attributes on the transformed data frame
    colnames(tr) <- varNames
    
    ## Output the data
    out1 <- data.frame(FieldName, tr)
    EOX
    editor:XY=460,320
    editor:Doc=Transposes an Data Frame
    end:R__Transpose_DF
    
    node:Static_Data_3
    bretype:core::Static Data
    editor:sortkey=58d532be43b56c18_3
    output:@40fe6c55598828e5/=
    prop:StaticData=<<EOX
    color:string, id:int,type:string,rand:int,junk:string
    Red,1,primary,27959,okay
    Green,2,secondary,27960,no
    EOX
    editor:XY=340,320
    end:Static_Data_3
    The R node is not distributed as part of the Lavastorm product due to licensing constraints, it needs to be downloaded separately. You can get the R node installer and the associated release notes that include the installation details from the Lavastorm website:

    http://www.lavastorm.com/product-downloads/

    You need to ensure you download the version of the installer that relates to the version of Lavastorm you are using *and* your Windows PC architecture.
    The r library needs to be added as a base library in BRE when using the R node example. The R node would then be visible in the palette in the interfaces and adaptors category.

    Regards,
    Adrian


  • 12.  RE: Python Node - 2D Array

    Employee
    Posted 03-30-2017 08:30

    Note: This was originally posted by an inactive account. Content was preserved by moving under an admin account.

    Originally posted by: stonysmith

    Originally posted by: jpstory
    					

    This is awesome!

    A minor issue is that except for _RowNum all the rest field names need to be input into Pivot - Names To Data node.

    The files I am dealing with between 1500 and 2000 fields ( I know, it's crazy), it'll be difficult to keep inputing the field names for every different report.

    That is why Python come up as an option, hope you understand.
    I do understand fully. Been there, bought the t-shirt.

    The code below should handle your data. There is an "opportunity" with our python node in that it can't read all the records, count them, and then re-wind for a second pass. So, I added a "cheat" where I first use an Agg to count the records and then send that count into Python as a secondary input. Ugly, but it works.

    Not trying to beat a drum here, but this method will work as long as you don't fill up RAM memory.. it is usually a 4gb limit.
    Separate issue: I would expect troubles with the BRDViewer if you have thousands of columns. Remember.. when you click on "View Data", then the file contents have to be transferred down to your PC - that can take time, and the BRDviewer may have some difficulty if the individual rows are too wide (too many columns).

    node:ArrayOperation_2
    bretype:core::Python
    editor:Label=ArrayOperation
    editor:sortkey=57b31c7215cd3f94
    input:57b31da559f00ea6/Input1=Static_Data.40fe6c55598828e5
    input:58dd0f8102d1537d/out1=Agg.40fd2c744c862db0
    output:57b32c5a64eb261c/Out1=
    prop:MaxRecdCnt=10
    prop:Python2Implementation=<<EOX
    import braininfo
    
    def setup(brainNodeControlObj, BrainNodeClass):
    	class BrainNode(BrainNodeClass):
    		def initialize(self):
    			super(BrainNode, self).initialize()
    
    			inRec = self.inputs[1].read()
    			self.rowCount = inRec["__Rows"]
    			metadata = self.newMetadata()
    			for i in range(self.rowCount):
    				metadata.append("Row_"+str(i+1),"String")
    			metadata.append("FieldName","String")
    			self.outputs[0].metadata = metadata
    			self.CurrentRow = 0
    			self.inRec = self.inputs[0].read()
    			self.colCount = len(inRec)
    			self.myArray = [["x" for C in range(self.colCount)] for R in range(self.rowCount)]
    			
    		def finalize(self, val):
    			outRec = self.outputs[0].newRecord()
    			for row in range(self.colCount-1):
    				fn=self.inputs[0].metadata[row]
    				outRec["FieldName"] = fn[0]
    				for col in range(self.rowCount):
    					outRec[col] = self.myArray[col][row]
    				self.outputs[0].write(outRec)
    			super(BrainNode, self).finalize(val)
    
    		def pump(self, quant):
    			while quant.permitsRunning(self):
    				if not self.inRec:
    					return False
    				for col in range(self.colCount-1):
    					self.myArray[self.CurrentRow][col] = self.inRec[col]
    				self.CurrentRow = self.CurrentRow+1
    				self.inRec = self.inputs[0].read()
    	return BrainNode
    EOX
    prop:RecordLength=5
    editor:XY=990,210
    editor:Notes=This node will evenly distribute the records in the input pin among all output pins until all output pin reaches the max threshold.
    editor:Notes=All the remaining records will be dumped into the last output pin
    editor:propdef=RecordLength|string|Input|ls.brain.node.transpose.RecLen|None
    editor:propdef=MaxRecdCnt|string|Input|ls.brain.node.transpose.MxRdCnt|None
    end:ArrayOperation_2
    
    node:Agg
    bretype:core::Agg
    editor:sortkey=58dd0f7c04c62348
    input:@40fd2c7427456e5b/=Static_Data.40fe6c55598828e5
    output:@40fd2c744c862db0/=
    prop:GroupBy=<<EOX
    1
    EOX
    prop:Script=<<EOX
    c=groupCount()
    emit c as __Rows
    where lastInGroup
    EOX
    editor:XY=900,240
    end:Agg
    
    node:Static_Data
    bretype:core::Static Data
    editor:sortkey=58d532be43b56c18
    output:@40fe6c55598828e5/=
    prop:StaticData=<<EOX
    color:string, id:int,type:string,rand:int,junk:string
    Red,1,primary,27959,okay
    Green,2,primary,7138,"""Let's Go"", he said"
    Blue,3,primary,15790,maybe
    Yellow,4,secondary,17809,nope
    Cyan,5,secondary,16901,Not NULL
    Magenta,6,secondary,15488,null
    Orange,7,tertiary,14584,NULL
    Chartreuse,8,tertiary,3465,True
    Aquamarine,9,tertiary,18650,1234
    Azure,10,tertiary,26377,23.4
    Violet,11,tertiary,27272,06/25/70
    Fuchsia,12,tertiary,6803,"June 25, 1970"
    Grue,13,fictional,7373,2:46 PM
    Bleen,14,fictional,22852,1.00001
    Octarine,15,fictional,11228,3.1415
    Garrow,16,fictional,26567,1.2.3.4.5
    Gendale,17,fictional,30176,
    Hooloovoo,18,fictional,12092,WieRD CaSED stRINg
    Fire,19,NULL,12345,14:46
    Ice,20,NULL,12345,2:46 PM 06/25/1970
    EOX
    editor:XY=650,290
    end:Static_Data