Python List Comprehensions Help

I wouldn't try to equate a dictionary to any more than it is.

A tool. That tool happens to be An unordered set of key value pairs. How and when you use that tool is up to you.

Just like List Comprehensions are a tool you can use, sometimes they are not the best tool to use, sometimes its a matter of preference. Either way it's good to know how to use the tool when you need it.

2 Likes
thisList=[]

#sum the data
for valueShift,valueDaily in zip(valuesSticks,valuesDailySticks):
	if type(valuesSticks) == int:
		thisList.append(valueShift+valueDaily)

valuesSticks
valuesDailySticks
thisList

output:

[14850, 23370, 23166, None, 18449, 35670, 0, 24741, 24600, 0, 0, 0, 0, None, 30075, 0, 20744]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[]

I donā€™t understand why this is not working.

thisList=[]

#sum the data
for valueShift,valueDaily in zip(valuesSticks,valuesDailySticks):
	if type(valuesSticks) != (str or NoneType):
		thisList.append(valueShift+valueDaily)
	else :
		thisList.append(valueDaily)

valuesSticks
valuesDailySticks
thisList

TypeError: unsupported operand type(s) for +: 'NoneType' and 'int'

I need them to line up.
I guess I canā€™t zip them or something if one has none type.

update
seems I can zip, but I canā€™t for loop them

So if I can list comprehension them instead, that might work?
I donā€™t know how to write this kind of list comprehension though

If you want to check the type of something, donā€™t use type() instead use isinstance().


thisList=[]

#sum the data
for valueShift,valueDaily in zip(valuesSticks,valuesDailySticks):
	if isinstance(valueShift,int): 
		thisList.append(valueShift+valueDaily)

valuesSticks
valuesDailySticks
thisList

1 Like

still didnt work

thisList=[]



#sum the data
for valueShift,valueDaily in zip(valuesSticks,valuesDailySticks):
	if isinstance(valuesSticks,int):
		thisList.append(valueShift+valueDaily)
	else :
		thisList.append(valueDaily)

valuesSticks
valuesDailySticks
thisList

output

[21600, 30750, 29484, None, 26763, 47970, 0, 35649, 29520, 0, 0, 0, 0, None, 32832, 0, 28400]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

I need to say like is not none maybe

or I have to pre-process the quality, generate a list of numbers of which ones are not good, then use that to construct the other list
Then my list would be so dynamic though, and I am not sure if I can line it up.
I will have to look at it on Monday, must be simpler.

At a glance valuesSticks isnā€™t an integer, from the name Iā€™m guessing itā€™s a list, which means youā€™re falling through and always executing thisList.append(valueDaily) which may well always be zero.

To my eye the code did exactly what it is expected.

1 Like

If youā€™re exploring python, then I have suggestions. There are many ways of doing things, so here are some, try them out, mix them up, and figure out what you prefer.

tuples are cool and easy to unpack, which can be useful with iterations:

machines = [
    ('S', [2,4,5,7,8,9,10,11,12]),
    ('A', [3,6,14,15,16,17,18])
]
base_tagpath = '[Test]M{0:02d}/M{0:02d}ProductionData/M{0:02d}{1}'
tagpaths = [base_tagpath.format(num, counter_type) for counter_type, nums in machines for num in nums]

base_snappath = '[Test]M{0:02d}/M{0:02d}ProductionData/Sticks Snapshot'
snappaths = [base_snappath.format(num) for _, nums in machines for num in nums]

dicts are also very cool and offer a neat way to group data. Lists of dicts are also cool.
Also, nesting comprehensions is not super intuitive at first but makes for a very concise way of looping through nested data structures:

paths = [
    {
        'tag': base_tagpath.format(num, counter_type),
        'snap': base_snappath.format(num)
    } for counter_type, nums in machines for num in nums
]

You can nest things until you get nauseous (donā€™t do this):

data = [
    {
        'value': tag.value,
        'snap': base_snappath.format(num)
    } for tag, num in zip(
        system.tag.readBlocking([base_tagpath.format(num, counter_type) for counter_type, nums in machines for num in nums]),
        (num for _, nums in machines for num in nums)
    )
]

And while weā€™re in a comprehension thread:

thisList = [vshift + vdaily if isinstance(vshift, int) else vdaily for vshift, vdaily in zip(sticks, daily_sticks)]
1 Like

How do you produce a list of the ā€˜iā€™ values that had good tags?

I can get the tag paths or values like this:

if 'Good' in str(qValue.quality):
		tagsOut.append(tag)

how would I get a list like these machines have good tags:

goodTagMachines=[1,2,4,5,8,9]

So I can use the list to get the machines and to designate which machines I am writing.
Struggling with trying to reference ā€˜iā€™ value inside the list comprehension.
I donā€™t see examples of it on the pages of instructions that I check.


I am struggling when a list has a Nonetype.

the old script had lines for each machine
if one errored for nonetype, then that machineā€™s lines were commented out with #):

#script starts with reading a bunch of values to a bunch of variables like this
M1Net=system.tag.read("[Test]department/products/M01/M01_Production/M01Net A Counter")

#at a certain time, if statement, a shift ends and a rolling total is captured for many machines
system.tag.writeBlocking(["[Test]department/products/M01/combined shifts/Net"],[system.tag.read("[Test]department/products/M01/combined shifts/Net").value+M1Net.value])

So I tried things like this that threw errors for type error unsupported none:

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

# at the specified time in an If statement
baseNetPaths='[Test]department/products/M{0:02d}/M{0:02d}_Production/{0:02d}Net{counter}
pathsNet=([baseNetPaths.format(i,counter=' S Counter') for i in exceptions]+
          [baseNetPaths.format(i,counter=' A Counter') for i in nonExceptons])

valueSticks=[tag.value for tag in system.tag.readBlocking(pathsNet)]

baseNetDailyPaths = '[Test]department/products/M{0:02d}/combined shifts/Net'
pathsNetDaily =( [baseNetDailyPaths.format(i) for i in exceptions ]+
                 [baseNetDailyPaths.format(i) for i in nonExceptions])

valuesDailySticks =[ tag.value   for tag in system.tag.readBlocking(pathNetDaily)]
valuesDailySticks

thisList=[]



#sum the data
for valueShift,valueDaily in zip(valuesSticks,valuesDailySticks):
	if valuesSticks!='NoneType':
		thisList.append(valueShift+valueDaily)
	else :
		thisList.append(valueDaily)

valuesSticks
valuesDailySticks
thisList

and it just threw errors because of nonetypes

So I think either I need to get a list of the good ā€˜iā€™ values, or I need to find a way to eliminate the nonetype error

enumerate.

goodMachines = [i for i, qval in enumerate(system.tag.readBlocking(paths)) if qval.quality.isGood()]

or, checking for None instead of qualities:

[i for i, thing in enumerate(things) if thing is not None]

But depending on what you're trying to do, matching paths with values in a dictionary as I described in a previous posts might be more useful.

1 Like

This type of logic doesn't work, or at the very lest it doesn't work the way you expect it to.

If you were truly wanting to write this as a conditional statement, what I think you were trying to do is this:

if type(valueSsticks) != str or type(valuesSticks) != NoneType:

However, as discussed use isinstance to check for the type of an object. Or in the case of NoneType you can just check that the variable is not None

Should Be:

for valueShift,valueDaily in zip(valueSticks,valuesDailySticks):
    if valuesSticks: #Logically equivilent to if valuesSticks is not None
       thisList.append(valueShift + valueDaily)
    else:
       thisList.append(valueDaily)

Or since this is a thread about comprehensions this can be written as one as well:

thisList = [valueShift + valueDaily if valuesSticks else valueDaily for valueShift,valueDaily in zip(valuesSticks,valuesDailySticks)]

To explain the syntax here, it takes advantage of inline if statements. Anytime you have a structure like:

if condition:
    thisVar = someThing
else:
   thisVar = someOtherThing

you can write it like this:

thisVar = something if condition else someOtherThing

So that will condence your for loop to this:

for valueShift, valueDaily in zip(valuesSticks,valuesDailySticks):
    thisList.append(valueShift + valueDaily if valuesSticks else valueDaily)

Which can be written as the comprehension above.

Another note, as has been stated already, be careful of your naming. valueShift and valuesSticks are very similar and pretty easy to transpose.

This:

for valueShift, valueDaily in zip(valuesSticks,valuesDailySticks):
    if valueShift:
        thisList.append(valueShift + valueDaily)
    else:
        thisList.append(valueDaily)

Is very different from what you have.

1 Like
Traceback (most recent call last):
  File "<input>", line 42, in <module>
TypeError: unsupported operand type(s) for +: 'com.inductiveautomation.ignition.common.model.values.BasicQualifiedValue' and 'int'

for


#sum the data
for valueShift,valueDaily in zip(valuesSticks,valuesDailySticks):
	if valuesSticks :
		thisList.append(valueShift+valueDaily)
	else :
		thisList.append(valueDaily)

valuesSticks
valuesDailySticks
thisList

line 42 is where it is appending the addition

Read my full post:

valuesSticks is a list, which could contain NoneType objects, but not be None itself.

valueShift is a member of that list.

for valueShift,valueDaily in zip(valuesSticks,valuesDailySticks):
    if valueShift:
        thisList.append(valueShift + valueDaily)
    else:
        thisList.append(valueDaily)

valuesSticks
valuesDailySticks
thisList
1 Like

using an ā€˜orā€™ can help with a null value:

list1 = [1,2,3,None,5]
list2 = [1,2,None,4,5]

sumList = [(a or 0) + (b or 0) for a, b in zip(list1, list2)]

print sumList

3 Likes

I messed up the valueShift and valuesSticks mixing up for sure

Thanks

That is amazing Jordan
Tried:

print valuesSticks
print valuesDailySticks
sumList = [(a or 0)+(b or 0) for a,b in zip(valuesSticks,valuesDailySticks)]
print sumList

output

[0, 30750, 50544, None, 83212, 50430, 0, 52615, 0, 0, 0, 0, 0, None, 68241, 0, 73622]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 30750, 50544, 0, 83212, 50430, 0, 52615, 0, 0, 0, 0, 0, 0, 68241, 0, 73622]

The ordering is preserved.
I donā€™t have to use enumerate to see the indexes of good and try to correlate them across the two lists of counters.

It is just like

base Path
Paths from list comprehension to get all the machines
list comprehension to read the values

repeat that for the daily

then just add them with the or

now I can write them with a list comprehension easy too


revised


print valuesSticks
print valuesDailySticks
sumList = [(a or 0)+(b or 0) for a,b in zip(valuesSticks,valuesDailySticks)]
print sumList



thisList=[]
for a,b in zip(valuesSticks,valuesDailySticks):
    if a:
        thisList.append(valueShift + valueDaily)
    else:
        thisList.append(valueDaily)
print 'here'
valuesSticks
valuesDailySticks
thisList
[0, 34440, 52650, None, 85227, 52890, 0, 55817, 0, 0, 0, 0, 0, None, 70575, 0, 76626]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 34440, 52650, 0, 85227, 52890, 0, 55817, 0, 0, 0, 0, 0, 0, 70575, 0, 76626]
here
[0, 34440, 52650, None, 85227, 52890, 0, 55817, 0, 0, 0, 0, 0, None, 70575, 0, 76626]
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
[0, 75595, 75595, 0, 75595, 75595, 0, 75595, 0, 0, 0, 0, 0, 0, 75595, 0, 75595]


oh I see it, I needed to reinitialize thisList =[]

You changed your loop variables but still used the old names in your loop.

for a,b in zip(valuesSticks,valuesDailySticks):
   if a:
       thisList.append(a + b)
   else:
       thisList.append(b)

Careful. Try to use meaningful names in code. Not only does it provide some documentation but it makes it easier to follow what is going on.

Perhaps:

for shiftSticks, dailySticks in zip(valuesSticks,valuesDailySticks):
    if shiftSticks:
        thisList.append(shiftSticks + dailySticks)
    else:
        thisList.append(dailySticks)
1 Like

I donā€™t understand how it worked with the old variables in there

oh, it kind of was stored in memory huh?

so if I closed the script console, then reopened it, then that would have thrown an error?


I appreciate the reminders to use relevant naming. I intend to.
Kind of scrambling to keep up with reading about things suggested, testing them, and trying new things.

1 Like

Yep. Even if you close it and reopen it in the same session. Using the reset button helps alleviate that.

2 Likes

I used len() to get the length of a list I have

it is 71 elements long

I want to test system.tag.writeBlocking()
What is the best way to write a tag that be written to in this way?

image

Do I just make an array type memory tag and write to it?

https://docs.inductiveautomation.com/display/DOC81/Tag+Data+Types#TagDataTypes-ArrayTags

I donā€™t see if I can do this. I will try.


was odd

system.tag.writeBlocking(testPath,valuesShift)
print system.tag.readBlocking(testPath)
[0, 0, 0, 83352, 96878, 0, None, 134915, ......]
71
[Good]
[[[Ljava.lang.Long;@*******, Good, Mon Mar 14 13:25:00 EDT 2022 (**********)]]

As you already saw, I donā€™t use arrays much. :wink:

To a dataset tag. This can let you see all the values a once.
EDIT: Technically, you could just use a single column, but the idx column gives a visual reference.

datasetOut = system.dataset.toDataSet(['idx', 'values'], [[i, value] for i, value in enumerate(listOfValues)])
system.tag.writeBlocking(['[provder]path/to/tag'], [datasetOut])
1 Like

image

Thanks

that worked great

the none value was ā€œwrittenā€ without an error

I was only a little concerned, but it is so nice to have a peaceful mind.

1 Like

For me, that ship has sailed. lol

1 Like