Python List Comprehensions Help

what if I have two different sets of base tags?
Like I can do an exception list.
something like:

exceptions =[2,4,5,8,9,10,11,12]
nonExceptions = [3,6,14,15,16,17,18]
counterS='NetSCounter'
counterA='NetACounter'
baseTag ='[Test]M{0:02d}/M{0:02d}ProductionData/M{0:02d}{counter}'


pathsNewNet=[ baseNetTags.format(i,i,i,k) for i in xrange(1,numMachines + 1) ]	



well that isn’t right

I don’t think I understand where you’re getting at…

I think I have a situation where I need two for clauses.

I have 18 machines.
Half of them need to use tags from s-counters and half from a-counters.

So I have to iterate through the 18 machines for my paths
I also have to set the counter type in the path for each.

I am looking in:

https://www.geeksforgeeks.org/string-formatting-in-python/
https://www.w3schools.com/python/python_lists_comprehension.asp
https://www.pythonforbeginners.com/basics/list-comprehensions-in-python

trying to stitch together a proper list comprehension
I saw on stackexchange someone recommended two for clauses, but then their example was hard to follow

I fail to see the issue…
Do you mean you want to do something like this ?

paths = (
	[baseTag.format(i, i, i, counterS) for i in exceptions]
	+ [baseTag.format(i, i, i, counterA) for i in nonExceptions]
)
1 Like

I think you were reading about nested for loops in one comprehension. Both expanded and comprehension forms in this example:

numMachines = 18
exceptions =[2,4,5,8,9,10,11,12]
nonExceptions = [3,6,14,15,16,17,18]
counters = ['NetSCounter', 'NetACounter'] 
baseNetTags ='[Test]M{machineNum:02d}/M{machineNum:02d}ProductionData/M{machineNum:02d}/{counter}'

pathsNewNet = pathsNewNet=[baseNetTags.format(machineNum=i,counter=k) for i in xrange(1,numMachines + 1) for k in counters]

print 'Comprehension:'
print pathsNewNet

expandedLoopTags = []
for i in xrange(1,numMachines + 1):
	for k in counters:
		expandedLoopTags.append(baseNetTags.format(machineNum=i,counter=k))

print '----------------'
print 'Expanded Loop:'
print expandedLoopTags
1 Like

I think I can help clarify this a little, although, this is just my outsider understanding of the issue and why I think the change was a good one.

In 7.9 there were 6 functions that worked with the tag system

  1. system.tag.read()
  2. system.tag.readAll()
  3. system.tag.write()
  4. system.tag.writeAll()
  5. system.tag.writeSyncronous()
  6. system.tag.writeAllSyncronous()

This was confusing for some users who expected similarly named functions to behave the same. For instance, system.tag.write() is an *asynchronous * function, meaning that it does not block the thread on which it is called. However, system.tag.read() is a synchronous function, meaning that it does block the thread on which it is called.

If you wanted a write operation to block the thread, then you needed to explicitly call system.tag.writeSyncronous().

There was no direct function for an asynchronous read and if you wanted to do that then you had to do it explicitly.

I think you can see how this could be confusing. In then end asynchronous or not, all of the functions eventually did one of two operations, readBlocking or writeBlocking, just sometimes that was wrapped inside of another function which made the operation asynchronous or not.

In order to clear up some of the potential confusion they removed the abstraction that was in place where it could be.

So we arrived at the now 4 functions with a clear meaning from just the naming convention.

  1. system.tag.readBlocking()
  2. system.tag.readAsync()
  3. system.tag.writeBlocking()
  4. system.tag.writeAsync()

Now when you call these functions you know what you get just from the name. readBlocking will block the thread, readAsync will not block the thread.

I don't know if there were any changes under the hood, but even so I feel the name change was a great move.

4 Likes

I call a script at 6:58am to sum net and gross quantities.
Do you recommend to use :
system.tag.readAsync()
and
system.tag.writeAsync()

?

and scenario 2
I have read a ton of things, do math, write to a ton of things
then it is recommended to use as few of blocking as I can?

I am researching when to use asych programming, but I am having a hard time with the abstractness/subjectiveness

What you use depends on what you need to happen in the script.

  1. Is the script you're writing dependent upon the values of the tags you're reading?
  2. If so, would waiting on the tags to be read, lock up a thread that a user is interacting with?

Off the top of my head, I would say the majority of the time you don't need anything other than readBlocking() and writeBlocking() the other functions are there for the times when you do need that special functionality.

I missed some other, probably important infomration in my last post. The biggest change, was the removal of the readAll(), writeAll() wrappers. Now the functions all take a list of tagPaths, and values if writing, removing the need for a readAllBlocking().

Always minimize the number of times you block a thread.

Scripts which I write that utilize the tag system always follow this pattern:

  1. read all tags which will be used in the script
  2. do the work
  3. write out all of the tags which need to be written.

Don't use 5 calls to readBlocking when you can use just one.

On the other hand don't go looking for optimizations just to look for optimizations. For instance look at this code snipit

from java.lang import System

values = range(1,100000)
test1Total = 0
test2Total = 0
test3Total = 0
for i in range(10):
	start = System.nanoTime()
	netTotal = sum([i for i in values if i % 2])
	end = System.nanoTime()
	
	test1Total += end - start
	
	start = System.nanoTime()
	netTotal = sum(i for i in values if i % 2)
	end = System.nanoTime()
	
	test2Total += end - start
	
	start = System.nanoTime()
	
	evenValues = []
	for i in values:
		if i % 2:
			evenValues.append(i)
	netTotal = sum(evenValues)
	end = System.nanoTime()
	
	test3Total += end - start
		
print 'Test 1 took an average of {} ms'.format(test1Total/10000000)
print 'Test 2 took an average of {} ms'.format(test2Total/10000000)
print 'Test 3 took an average of {} ms'.format(test3Total/10000000)

I ran this 10 times on my system, here is the output:

>>> 
Test 1 took an average of 14 ms
Test 2 took an average of 5 ms
Test 3 took an average of 15 ms
>>> 
Test 1 took an average of 24 ms
Test 2 took an average of 4 ms
Test 3 took an average of 27 ms
>>> 
Test 1 took an average of 14 ms
Test 2 took an average of 4 ms
Test 3 took an average of 15 ms
>>> 
Test 1 took an average of 24 ms
Test 2 took an average of 4 ms
Test 3 took an average of 26 ms
>>> 
Test 1 took an average of 24 ms
Test 2 took an average of 4 ms
Test 3 took an average of 28 ms
>>> 
Test 1 took an average of 24 ms
Test 2 took an average of 4 ms
Test 3 took an average of 26 ms
>>> 
Test 1 took an average of 24 ms
Test 2 took an average of 4 ms
Test 3 took an average of 26 ms
>>> 
Test 1 took an average of 23 ms
Test 2 took an average of 5 ms
Test 3 took an average of 26 ms
>>> 
Test 1 took an average of 14 ms
Test 2 took an average of 5 ms
Test 3 took an average of 15 ms
>>> 
Test 1 took an average of 14 ms
Test 2 took an average of 5 ms
Test 3 took an average of 15 ms
>>> 

All three tests have the exact same end result, and it's clear which code executes the quickest, however, when you're trouble shooting an issue are you generally looking for a code change which gives you a 10-20 ms boost? No, you're usually looking for more.

I'm not saying not to worry about performance, I'm saying you have to balance what it's worth spending your time on.

2 Likes

The real problem is that programmers have spent far too much time worrying about efficiency in the wrong places and at the wrong times ; premature optimization is the root of all evil (or at least most of it) in programming.

Good advice. The only times I would say its not true is if the optimization is obvious and easy (ie indexing a db table on a column you know will be used a lot) or if the optimization happens to also align with good design/coding (in this case, using one call to readBlocking instead of 5 - this will make the code simpler and more easily maintainable).

Beyond these two things, its hard to tell what will be a bottleneck that requires optimization prior to it happening.

2 Likes

I ran the test on a subsection of my code and I got 2ms with a bunch of read calls then 0ms with the single read call.

I thought the way the script ran before had caused a crash though.
At the time of running the script, I saw a crash on the gateway.

I don’t know how why the timing of the script would be the same as the crash though if the time was only like 20ms at worst case.

I am a little concerned I revised the script and didn’t really get as much benefit as hoped.


At least my code is now more robust though as even if the tags are not reading correctly the code will run.

ah, I can’t tell if that helps either. Before the script would error out. Then we wouldn’t have any numbers totaled. Now if a tag is bad, we get all the numbers except for the machine that had bad numbers. So we might miss this now.

If you’re worried about missing information about bad tag quality, you can be more specific about how you handle quality.

for example, if you want to filter out tags that don’t exist, but retain tags that do exist but returned a bad quality (note that I don’t have ignition on this computer, so I can’t test if everything works, but I hope you’ll get the point):

tags = [
	{
		'path': path,
		'qval': tag
	} for path, tag in zip(paths, system.tag.readBlocking(paths)) if tag.quality.code == 519
]

total = sum(tag['qval'].value for tag in tags if tag['qval'].quality.isGood())
bad_tags = [tag['path'] for tag in tags if tag['qval'].quality.isNotGood()]
1 Like

I really appreciating seeing how to do multiple lists.

update, figured this out.
I don’t know how to strikeout, but now sorting makes sense.
The two list example did not order them 1-17.
It had one set, then the next.

How is the sorting working?
Once with two lists, the suffix was ignored, and the results were sorted 1-17

Once with four lists, the suffix order was maintained and results were sorted 1-17 of each in order gross, net, sticks, standard volumes

I am happy with the sorting both last time and this time.
I want to know how it worked in both cases though. I expected one of them to be in a different order.

For different counters on the end of paths, they were in the correct order 1-17 with different suffixes.

paths = (	[baseTag.format(i, i, i, counterS) for i in exceptions]+
            [baseTag.format(i, i, i, counterA) for i in nonExceptions])

Results were in sequence from 1-17 each with their unique suffix counter in the path

In this example, my paths came out in the order that they were put in.
1-17 gross
1-17 net
1-17 sticks
1-17 standard volumes
If it were just ascii, then I think Standard would be before Sticks.

I used this to check them all.
Below is a snip of some of the results.

numMachines=18
baseGrossPaths='[Test]here/M{0:02d}/Combined Shifts/Gross'
#then the next three base paths

paths = ([baseGrossPaths.format(i)          for i in xrange(1,numMachines + 1) if i not in exclusions]+
		 [baseNetPaths.format(i)            for i in xrange(1,numMachines + 1) if i not in exclusions]+
		 [baseSticksPaths.format(i)         for i in xrange(1,numMachines + 1) if i not in exclusions]+
		 [baseStandardVolumePaths.format(i) for i in xrange(1,numMachines + 1) if i not in exclusions])
paths[0]
paths[1]
paths[2]
paths[3]
paths[4]
paths[5]
paths[6]
paths[7]
paths[8]
paths[9]
paths[10]
paths[11]
paths[12]
paths[13]
paths[14]
paths[15]
paths[16]
paths[17]
paths[18]
paths[19]
paths[20]
paths[21]
paths[22]
paths[23]
paths[24]
paths[25]
paths[26]
paths[27]
paths[28]
paths[29]
paths[30]
paths[31]
paths[32]
paths[33]
paths[34]
paths[35]
paths[36]
paths[37]
paths[38]
paths[39]
paths[40]
paths[41]
paths[42]
paths[43]
paths[44]
paths[45]
paths[46]
paths[47]
paths[48]
paths[49]
paths[50]
paths[51]
paths[52]
paths[53]
paths[54]
paths[55]
paths[56]
paths[57]
paths[58]
paths[59]
paths[60]
paths[61]
paths[62]
paths[63]
paths[64]
paths[65]
paths[66]
paths[67]

Values17by4Spaces = [0]*17*4
Values17by4Spaces

image

First… may i suggest not copying 67 lines of print ? :smiley:

print '\n'.join(paths)

Lists keep their order, dicts don’t. If you end up with two different ordering in lists, then you didn’t put the elements in in the same order.

1 Like

Surround the text with html-ish <s>...</s>.
This in the comment editor:

<s>This is a strikeout test.</s>  This is normal.

Becomes this:

1 Like

thanks very much for the
print '\n'.join(paths)
That will save me some excel time for sure.

I got this list while testing and trying to do the next thing

notGoodTags =[ p   for p in system.tag.readBlocking(paths) if 'Good' not in str(p.quality)     ]

It didn’t tell me which machines had the bad tags though.


I am working on a situation like this right now:

17 variables = 17 reads
if statement
    17 writes of the 17 variables

I think my solution is like:
read the good values to a list
append a list of i values that were good
generate the writing paths based on the good i values
check if the lists match
write from one list to the other

trying this, but it gives me the tags, and I want to get the numbers

numMachines = 18
exceptions =[2,4,5,8,9,10,11,12]
nonExceptions = [3,6,14,15,16,17,18]

baseTag ='[Test]M{0:02d}/M{0:02d}ProductionData/M{0:02d}{counter}'

paths = ([baseTag.format(i, i, i, counter='S') for i in exceptions] +
         [baseTag.format(i, i, i, counter='A') for i in nonExceptions])
goodTagsForSticks =[ i for in system.tag.readBlocking(paths) if i.quality.good]


I think could do this with Jordan’s demonstration above several posts.
I was trying to do it within a list comprehension nested for loop, but maybe that is not the way.

Update:

goodSticksTagsMachines =[ p   for p in system.tag.readBlocking(paths)     ]
goodSticksTagsMachines

goodtags= [k for k in exceptions if 'Good' not in goodSticksTagsMachines ]
goodtags

I got it
this did not work

I’m not quite sure what you’re trying to do… But a few things:

baseTag ='[Test]M{0:02d}/M{0:02d}ProductionData/M{0:02d}{counter}'
baseTag.format(i, i, i, counter='S')

You don’t need to repeat the i here. you’re using {0:02d} which means ‘use the first argument’, so they’re all using the first i you’re passing. Which I expect is the reason you named the {counter} one.

baseTag ='[Test]M{0:02d}/M{0:02d}ProductionData/M{0:02d}{1}'
baseTag.format(i, 'S')

will do the same thing.

goodSticksTagsMachines = [p for p in system.tag.readBlocking(paths)]

You don’t need a list comprehension here. What this does is take each element in the list, then make another list with them.

goodTagsForSticks = [ i for in system.tag.readBlocking(paths) if i.quality.good]

This will only filter out the tags that have a bad quality. You need to get the value from them if you want the numbers:

goodTagsForSticks = [tag.value for tag in system.tag.readBlocking(paths) if tag.quality.good]

I encourage you to use more descriptive names for your variables, it might help you catch logical flaws like this one

Thanks again Pascal


regarding:
goodTagsForSticks = [tag.value for tag in system.tag.readBlocking(paths) if tag.quality.good]
Is there a good method to get which machine numbers have bad tags from within a single list comprehension, or is it best to make lists and run the check, then append?

I am switching to designating tag when I want the tag, thanks for the recommendation.
In this case though, I really wanted to know the number of the machine, and didn’t figure out how to get it without making a list, checking against it, and appending/writing a new list.


I am reading 17 machine values to write 17 value snapshots
Right now the script:

17 variables = 17 reads
if statement 
    17 writes of the 17 variables that were read

my solution

numMachines = 18
exceptions =[2,4,5,7,8,9,10,11,12]
nonExceptions = [3,6,14,15,16,17,18]

baseTag ='[Test]M{0:02d}/M{0:02d}ProductionData/M{0:02d}{counter}'

pathsStickValues = ([baseTag.format(i,counter='S') for i in exceptions] +
                    [baseTag.format(i,counter='A') for i in nonExceptions])

valuesSticks= [i.value for i in system.tag.readBlocking(pathsStickValues)]

baseSticksSnapshots ='[Test]M{0:02d}/M{0:02d}ProductionData/Sticks Snapshot'
pathsSticksSnapshots=([baseSticksSnapshots.format(i) for i in exceptions]+
	                  [baseSticksSnapshots.format(i) for i in nonExceptions])

print '\n'.join(pathsSticksSnapshots)
#system.tag.writeblocking(pathsSticksSnapshots,valueSticks)  

updated

valuesSticks= [i.value for i in system.tag.readBlocking(pathsStickValues)]
was
valuesSticks= [i.value for i in system.tag.readBlocking(paths)]

I still have no idea what you’re trying to do :smiley:
What are ‘sticks’ ? What are ‘snapshots’ ?

pathsSticksSnapshots=([baseSticksSnapshots.format(i) for i in exceptions]+
	                  [baseSticksSnapshots.format(i) for i in nonExceptions])
# could do this instead:
pathsSticksSnapshots = [baseSticksSnapshots.format(i) for i in exceptions + nonExceptions]
2 Likes

sticks/pieces/products

just a name for products in this case

snapshots, well that is a name that some people used to describe a scenario where they wanted put a value that was changing and kind of capture how it was at a certain time like a freezeframe/snapshot is a still picture of life.

probably has to be a really odd thing to explain if English isn’t a native language

It combines idioms and words that formerly were used more frequently.

We end up making new meanings for words that aren’t very common.

Then later, words will be even more confusing because they will have so many more meanings. haha


unless you meant more generally what am I doing?

I am learning a ton and revising some scripts that were causing issues on my gateway so I can add a couple more machines we need and smooth performance both.

Personally, I’d likely use a dictionary to store lookup information. In your case, it’s just one thing to look up, a counter type; however, you can easily add other lookup information as needed.

# Lookup dictionary. Note the ones that are commented out.
machineDict = {
               #1  : {'counterType'   : None},
               2  : {'counterType' : 'S'},
               3  : {'counterType' : 'A'},
               4  : {'counterType' : 'S'},
               5  : {'counterType' : 'S'},
               6  : {'counterType' : 'A'},
               7  : {'counterType' : 'S'},
               8  : {'counterType' : 'S'},
               9  : {'counterType' : 'S'},
               10 : {'counterType' : 'S'},
               11 : {'counterType' : 'S'},
               12 : {'counterType' : 'S'},
               #13 : {'counterType' : None},
               14 : {'counterType' : 'A'},
               15 : {'counterType' : 'A'},
               16 : {'counterType' : 'A'},
               17 : {'counterType' : 'A'},
               18 : {'counterType' : 'A'}
              }

baseTag = '[Test]M{0:02d}/M{0:02d}ProductionData/M{0:02d}{1}'
baseSticksSnapshots = '[Test]M{0:02d}/M{0:02d}ProductionData/Sticks Snapshot'

# You can create your input and output tag lists in one iteration. 
# Just because you can use a  comprehension doesn't always mean that you should. ;)
pathsStickValues, pathsSticksSnapshots = [], []
for i in machineDict.keys():
	pathsStickValues.append = baseTag.format(i, machineDict[i]['counterType'])
	pathsSticksSnapshots.append = baseSticksSnapshots.format(i)
	
valuesSticks = [i.value for i in system.tag.readBlocking(paths)]

An example of a more complex lookup dictionary, just to illustrate. This particular one is used to parse a bill of materials into something more useful for the assembly line.

bomLookupDict = {
                 'dcm'        : {'desc'       : ['DCM']},
                 'psat'       : {'desc'       : ['Pressure']},
                 'harness'    : {'desc'       : ['Harness']},
                 'latch'      : {'desc'       : ['Latch']},
                 'speaker'    : {'desc'       : ['Speaker']},
                 'antenna'    : {'desc'       : ['Antenna']},
                 'module'     : {'desc'       : [
                                                 'Assem Frt',
                                                 'Asm Frt',
                                                 'AssemFrt',
                                                 'AsemFrtSht',
                                                 'AssemFrtSht'
                                                ],
                                 '68223827AI' : {'type'      : 'long', 
                                                 'drive'     : 'manual',
                                                 'handle'    : 'BLACK',
                                                 'lock_knob' : 'BLACK'
                                                 },
                                 '68429127AI' : {'type'      : 'long',
                                                 'drive'     : 'manual',
                                                 'handle'    : 'BLACK',
                                                 'lock_knob' : 'BLACK'
                                                 },
                                 '68429135AD' : {'type'      : 'long',
                                                 'drive'     : 'manual',
                                                 'handle'    : 'BLACK',
                                                 'lock_knob' : 'BLACK'
                                                },
                                 '68501197AA' : {'type'      : 'long',
                                                 'drive'     : 'power',
                                                 'handle'    : 'BLACK',
                                                 'lock_knob' : 'BLACK'
                                                },
                                 '68429077AA' : {'type'      : 'long',
                                                 'drive'     : 'power',
                                                 'handle'    : 'SZ0',
                                                 'lock_knob' : 'BLACK'
                                                },
                                 '68429075AA' : {'type'      : 'long',
                                                 'drive'     : 'power',
                                                 'handle'    : 'SZ0',
                                                 'lock_knob' : 'SZ6'
                                                },
                                 '68429081AA' : {'type'      : 'long',
                                                                'drive'     : 'power',
                                                                'handle'    : 'SZ6',
                                                                'lock_knob' : 'SZ6'
                                                               },
                                 '68568935AA' : {'type'      : 'long',
                                                               'drive'     : 'manual',
                                                               'handle'    : 'BLACK',
                                                               'lock_knob' : 'BLACK'
                                                               },
                                 '68568939AA' : {'type'      : 'long',
                                                               'drive'     : 'power',
                                                               'handle'    : 'BLACK',
                                                               'lock_knob' : 'BLACK'
                                                               },
                                 '68568945AA' : {'type'      : 'long',
                                                               'drive'     : 'power',
                                                               'handle'    : 'SZ6',
                                                               'lock_knob' : 'SZ6'
                                                               },
                                 '68429087AA' : {'type'      : 'short',
                                                 'drive'     : 'power',
                                                 'handle'    : 'BLACK',
                                                 'lock_knob' : 'BLACK'
                                                },
                                 '68429091AA' : {'type'      : 'short',
                                                 'drive'     : 'power',
                                                 'handle'    : 'SZ6',
                                                 'lock_knob' : 'SZ6'
                                                },
                                 '68429099AA' : {'type'      : 'short',
                                                 'drive'     : 'power',
                                                 'handle'    : 'SZ0',
                                                 'lock_knob' : 'SZ0'
                                                },
                                 '68429093AA' : {'type'      : 'short',
                                                 'drive'     : 'power',
                                                 'handle'    : 'TZ3',
                                                 'lock_knob' : 'SZ6'
                                                },
                                 '68429095AA' : {'type'      : 'short',
                                                 'drive'     : 'power',
                                                 'handle'    : 'TZ6',
                                                 'lock_knob' : 'SZ6'
                                                },
                                 '68429097AA' : {'type'      : 'short',
                                                 'drive'     : 'power',
                                                 'handle'    : 'TZS',
                                                 'lock_knob' : 'SZ6'
                                                },
                                 '68526619AA' : {'type'      : 'short',
                                                 'drive'     : 'power',
                                                 'handle'    : 'SKS',
                                                 'lock_knob' : 'SZ6'
                                                },
                                 '68429089AA' : {'type'      : 'short',
                                                 'drive'     : 'power',
                                                 'handle'    : 'SZ0',
                                                 'lock_knob' : 'BLACK'
                                                },
                                 '68570689AA' : {'type'      : 'short',
                                                 'drive'     : 'power',
                                                 'handle'    : 'BLACK',
                                                 'lock_knob' : 'BLACK'
                                                },
                                 '68570723AA' : {'type'      : 'short',
                                                 'drive'     : 'power',
                                                 'handle'    : 'SZ6',
                                                 'lock_knob' : 'SZ6'
                                                },
                                 '68570735AA' : {'type'      : 'short',
                                                 'drive'     : 'power',
                                                 'handle'    : 'TZ3',
                                                 'lock_knob' : 'SZ6'
                                                },
                                 '68570737AA' : {'type'      : 'short',
                                                 'drive'     : 'power',
                                                 'handle'    : 'TZ6',
                                                 'lock_knob' : 'SZ6'
                                                },
                                 '68570739AA' : {'type'      : 'short',
                                                 'drive'     : 'power',
                                                 'handle'    : 'TZS',
                                                 'lock_knob' : 'SZ6'
                                                },
                                 '68570741AA' : {'type'      : 'short',
                                                 'drive'     : 'power',
                                                 'handle'    : 'SKS',
                                                 'lock_knob' : 'SZ6'
                                                }
                                }
                }
4 Likes