Converting CNC drillings from old to new system (using Python) - python

I have this kind of file (part):
H DX=615 DY=425 DZ=22.15 -AB C=0 T=0 R=999 *MM /"def" BX=2.5 BY=452.5 BZ=25 ;M20150710.
XBO X=100 Y=50 Z=5 V=1000 R=0 x=0 y=0 D=10 N="P" F=1 ;Test F1/10P.
...
which I want to convert to a new programming system. What I want to do is first read the header (H) and put the DX, DY and DZ values in respectively named variables. I managed to do this, but when I came to process my XBO line (a drilling, from which I need X, Y, Z, V, R, x, y, D, N, F and ;, also in separate variables) my code started looking very ugly very fast.
So I started over, and came up with this:
f = open("input.xxl") # open input file
for line in f:
if Debug==1: print line
for char in line:
charbuffr=charbuffr+char
if "H" in charbuffr:
if Debug==1: print'HEADER found!'
charbuffr=""
if "XBO" in charbuffr:
if Debug==1: print'XBO found!'
charbuffr=""
This correctly identifies the separate commands H and XBO, but I'm kind of stuck now. I can use the same method to extract all the variables, from loops inside the H and XBO loops, but this does not seem like good coding...
Can anyone set me on the right foot please? I don't want a full solution, as I love coding (well my main job is coding for CNC machines, which seems easy now compared to Python), but would love to know which approach is best...

Instead of converting data types by hand, you could use ast. literal_eval. This helper function takes a list of the form ['a=2', 'b="abc"'] and converts into a dictionary {'a': 2, 'b': 'abc'}:
import ast
def dict_from_row(row):
"""Convert a list of strings in the form 'name=value' into a dict."""
res = []
for entry in row:
name, value = entry.split('=')
res.append('"{name}": {value}'.format(name=name, value=value))
dict_string = '{{{}}}'.format(', '.join(res))
return ast.literal_eval(dict_string)
Now parsing the file becomes a bit simpler:
for line in f:
row = line.split()
if not row:
continue
if row[0] == 'H':
header = dict_from_row(row[1:4])
elif line[0] == 'XBO':
xbo = dict_from_row(row[1:11])
Results:
>>> header
{'DX': 615, 'DY': 425, 'DZ': 22.15}
>>> xbo
{'D': 10, 'F': 1, 'R': 0, 'V': 1000, 'X': 100, 'Y': 50, 'Z': 5, 'x': 0, 'y': 0}

As an inspiration, you can do something like this:
for raw_line in f:
line = raw_line.split()
if not line:
continue
if line[0] == 'H':
header = {}
for entry in line[1:4]:
name, value = entry.split('=')
header[name] = float(value)
elif line[0] == 'XBO':
xbo = {}
for entry in line[1:11]:
name, value = entry.split('=')
try:
xbo[name] = int(value)
except ValueError:
xbo[name] = value[1:-1] # stripping of the ""
Now headercontains the extensions of your domain:
{'DX': 615.0, 'DY': 425.0, 'DZ': 22.15}
and xbo the other values:
{'D': 10,
'F': 1,
'N': 'P',
'R': 0,
'V': 1000,
'X': 100,
'Y': 50,
'Z': 5,
'x': 0,
'y': 0}
Access the individual values in the dictionaries:
>>> header['DX']
615.0

Related

Why do I get Type Error on one line, but not the others

So, I have this piece of code which is used to add/subtract from the x and y value in a list with dictionaries. The thing is, the code must account for the fact that the dictionary can be given like a string with quotes around it. So, for that reason I use ast.literal_eval(point) to turn the string into a dictionary. However, for some reason with this line that has been changed following commands don't work like they do with the others.
Commands in question: point['x'] += offset['x'] and point['y'] += offset['y']
I am very confused, both with the issue in question and the code in general as it was translated from js.
import ast
def parse(point):
if type(point) == dict:
return point
else:
return ast.literal_eval(point)
def shift(offset, points):
modified_points = points.copy()
for point in modified_points:
arg_type = type(point)
parse(point)
point['x'] += offset['x']
point['y'] += offset['y']
print(point)
return modified_points
polyline = [
{'x': 0,'y': 0},
{'x': 10, 'y': 10},
'{"x": 20, "y": 20}',
{'x': 30, 'y': 30}
]
results = shift({'x': 10, 'y': -5}, polyline)
print(results)
The error goes as following:
File "/home/selby/PycharmProjects/10thPractical/3rd.py", line 37, in <module>
results = shift({'x': 10, 'y': -5}, polyline)
File "/home/selby/PycharmProjects/10thPractical/3rd.py", line 19, in shift
point['x'] += offset['x']
TypeError: string indices must be integers
Process finished with exit code 1
You do not store the return value when you call parse(point), i.e. point is still a string when you try to use it in point['x'] += offset['x'] (as the error indicates).
Replace the line with point = parse(point), and the error will disappear.

How to avoid overwriting the data from a list to a dictionary using for loop?

I have a nested dictionary as follows:
bus = dict()
pvsystem = dict()
for j in range(500):
bus[j] = {'vm': {'a': 1, 'b': 1, 'c': 1}, 'va': {'a': 1, 'b': 1, 'c': 1}}
nw = dict()
for step in range(24):
c = step + 1
nw[str(c)] = {'bus': bus}
solution = {'nw': nw}
results = {'solution': solution}
I am using a for loop to fill up the values in the nested dictionary as follows:
for step in range(10):
c = step + 1
for b in range(20):
AllpuVmVa = dss.Bus.puVmagAngle()
results['solution']['nw'][str(c)]['bus'][b]["vm"]['a'] = AllpuVmVa[0]
results['solution']['nw'][str(c)]['bus'][b]["va"]['a'] = AllpuVmVa[1]
print("Step: ", c, " Voltages: ", AllpuVmVa)
AllpuVmVa is a list. Its values are changed once the step changed (It is defined based on function outside the loop).
Here, using the print function, it is clear that the values of AllpuVmVa at each step are different, but the values stored in (results['solution']['nw'][str(c)]['bus'][b]["vm"]['a']) and (results['solution']['nw'][str(c)]['bus'][b]["va"]['a']) are the same for all the steps, which is equal to the last step. It sounds that there is overwriting for the data.
Is there any idea to fix this issue?
The problem is, that you assign the same dictionary stored in bus to every value in nw dict. To fix the issue, you can make new bus dictionary every iteration. For example:
bus = dict()
pvsystem = dict()
def get_bus():
return {j: {'vm': {'a': 1, 'b': 1, 'c': 1}, 'va': {'a': 1, 'b': 1, 'c': 1}} for j in range(500)}
nw = dict()
for step in range(24):
c = step + 1
nw[str(c)] = {'bus': get_bus()} # <-- use get_bus() here
solution = {'nw': nw}
results = {'solution': solution}

python naive string token matcher

I've written a very naive token string search matcher. It's a little too naive though, as with the following code, it would bring back every artists in the artists list, due to how 'a r i z o n a' is tokenised.
import collections
import re
def __tokenised_match(artist, search_artist):
matches = []
if len(re.split(r'[\\\s/-]', search_artist)) > 1:
a = [artist.sanitisedOne, search_artist]
bag_of_words = [ collections.Counter(re.findall(r'\w+', words)) for words in a]
sumbags = sum(bag_of_words, collections.Counter())
print(sumbags)
for key, value in sumbags.items():
if len(re.findall(r'\b({k})\b'.format(k=key), search_artist)) > 0 and value > 1:
matches.append(artist)
if len(matches):
return matches
artists = [
{ 'artist': 'A R I Z O N A', 'sanitisedOne': 'a r i z o n a'},
{ 'artist': 'Wutang Clan', 'sanitisedOne': 'wutang clan'}
]
search_artist = 'a r i z o n a'
for artist in artists:
print(__tokenised_match(artist, search_artist))
this'll create a sumbags like this:
Counter({'a': 4, 'r': 2, 'i': 2, 'z': 2, 'o': 2, 'n': 2})
Counter({'a': 2, 'wutang': 1, 'clan': 1, 'r': 1, 'i': 1, 'z': 1, 'o': 1, 'n': 1})
this is kind of edge casey, but i wonder how i can tighten up against this kind of edge case. it would be fine for 'wutang clang' to match, but when it's single letters like this... it's a little much and will bring back every artist due to a matching twice.
The basic problem is that you return success on only a single match. This will kill your accuracy for any artist with an easily matched token in the name. We could tune your algorithm for matching a certain percentage of words, or for doing a bag-of-letters, intersection-over-union ratio, but ...
I recommend that you use something a bit stronger, such as string similarity, which is easily found in Python code. Being already packaged, it's much easier to use than coding your own solution.

Accessing dictionary key with multiple values

I have an assignment and for the first part, I am to access a text file which will have a list of production rules. I created a list of dictionaries from this text file:
x = y
x = y x
y = 0
y = 1
that looks like this:
myList = [{'x': 'y'}, {'x':'y x'}, {'y': 0}, {'y': 1}]
I want to find all the possible outputs when applying these rules. I am going to attempt to write code later that will go through and replace the nonterminal values and output a bunch of binary. However, for this dictionary:
{'x': 'y x'}
'y x' is all one string so I cannot replace y or x with anything unless I explicitly say
'y x' = some value
I have written this code and written a really bad test code to see if the computer can see if a value for a key exists:
prodList = []
for line in open('name of file', "r"):
line = line.strip()
lhs, rhs = line.split(' = ')
myList.append({lhs:rhs})
if 'y' in myList[0].values():
print True
Now if I run this it will print True and I could move on, but I can't seem to write code where if I wrote:
if 'y' in myList[1].values():
print True
that it would be True.
I tried writing
myList.append({lhs:rhs.split()})
But that didn't help and I couldn't check for any values at all. Is there any way that I could have the list look like this:
myList = [{'x': 'y'}, {'x':'y', x'}, {'y': 0}, {'y': 1}]
So that if I wrote
if 'y' in myList[1].values():
print True
it would return True?
If this sounds confusing, please let me know so I can try to clarify more.
I also tried to make a dictionary instead of a list of dictionaries by doing this:
for line in open('file.txt', "r"):
line = line.strip()
lhs, rhs = line.split(' = ')
myDict[lhs] = rhs
but when I printed the dictionary, I only got this:
{'y': 1, 'x': 'x y'}
I'm sure there is a better way to do this but I can't seem to figure out a way that works.
I looked over the above code again and I was just looking at the list of values and not the values themselves.
My question now is how do I make the dictionary with multiple values for one key? When I run this code:
for line in open(fileName, "r"):
line = line.strip()
lhs, rhs = line.split(' = ')
prodList[lhs] = rhs.split()
print prodList
I end up with just this:
{'y': [1], 'x': ['y', 'x']}
I'm not sure as to how I get this
myList = {'x': ['y'], 'x':['y', 'x'], 'y': [0], 'y':[1]}

Testing all Letters?

I am working on a script and the script should read a text file and test to see if the specified letters(a,a,r,d,v,a,r,k) are on each line. Im having a problem as I am trying to check for 3 different a's instead of just one. My code is below:
#Variables
advk = ['a','a','r','d','v','a','r','k']
textfile = []
number = 0
life = 0
for line in open('input.txt', 'rU'):
textfile.append(line.rstrip().lower())
while life == 0:
if all(word in textfile[number] for word in advk):
printed = number + 1
print ("Aardvark on line " + str(printed))
number+=1
if number == len(textfile):
life+=1
else:
number+=1
Everytime you want to count something in python, keep the Counter class in mind.
from collections import Counter
advk = Counter(['a','a','r','d','v','a','r','k'])
with open('input.txt', 'rU') as file:
for i, line in enumerate(file.readlines()):
if not advk - Counter(line.lower()):
print ("Aardvark on line " + str(i+1))
Given the input line
dffdaardvarksdsda
the Counter would look like these
Counter({'d': 5, 'a': 4, 'f': 2, 's': 2, 'r': 2, 'k': 1, 'v': 1})
and
Counter({'a': 3, 'r': 2, 'd': 1, 'k': 1, 'v': 1})
for your list of letters to search.
We use a trick by simply substracting the two Counters advl - Counter(line.lower()) and check if the resulting Counter has no elements left.
Other things to note:
You can use the with statement to ensure your file gets closed.
You can use enumerate instead counting the line numbers.
if advk list is variable and contents are read from somewhere else then to maintain unique elements in the list you can convert it to set and check.
advk = ['a','a','r','d','v','a','r','k']
advk = list(set(advk))
This makes advk a unique list and avoids check multiple 'a's in the line.
# If the line "ardvrk" should match, this is a solution:
chars=set('aardvark')
for nr, line in enumerate(open('input.txt', 'rU')):
if not chars-set(line): # is subset?
print 'match', nr, line,

Categories