4

I have a list of tuples as coordinates (it has to be this way)

points = [(x1, y1), (x2, y2), ...]

to draw a polygon in matplotlib. To get these coordinates I first created an empty list points = [] and then wrote a function to calculate each point from the coordinates of the centre, number of sides, side length and angle of rotation. After the function I wrote a code to read the above initial values from user's input and check their validity, and then call the function if the check is successful.

Now I want to store the coordinates and the number of points in a file as follows:

number of points
x1, y1
x2, y2
...
xn, yn

where each coordinate is written to 3 decimal places. Therefore, I need to format my tuples to 3 decimals, then convert them to strings and then write them in a file, and I want it in the shortest possible way.

I thought I would do something like lines = [float("{:.3f}".format(j)) for j in points] (which doesn't work since I have tuples) and then

lines.insert(0, len(points))
with open('points.txt', 'w') as f:
f.writelines("%s\n" % l for l in lines)

The above solution seems very nice to me, but I can't find a way to do the first line (formatting to decimals) for tuples, so I was wondering how could I possibly format a list of tuples to decimals to store them in a list for the following use of writelines and conversion into strings? Or if there is a shorter and better way of doing this, I would appreciate any hints. Thank you!

Patrick Artner
  • 50,409
  • 9
  • 43
  • 69
spfortray
  • 91
  • 1
  • 7
  • you want to convert the x and y coord of each point to 3 dec places right? – user2736738 Nov 15 '18 at 07:34
  • Yes, exactly. And I'm not even sure how to refer to each of them in the `points`. I mean, for example {0} is (x1, y1), but how do I refer to x1 alone? Maybe if I'd understand that it would help, but I have a feeling that the solution would rather lie in conversion of the whole `points` list. – spfortray Nov 15 '18 at 07:38

3 Answers3

5

You can directly write the floats into your file:

Testdata:

import random

tupledata = [(random.uniform(-5,5),random.uniform(-5,5) ) for _ in range(10)]
print(tupledata)

Output:

[(1.4248082335110652, 1.9169955985773148), (0.7948001195399392, 0.6827204752328884),
 (-0.7506234890561183, 3.5787165366514735), (-1.0180103847958843, 2.260945997153615), 
 (-2.951745273938622, 4.0178333333006435), (1.8200624561140613, -2.048841087823593), 
 (-2.2727453771856765, 1.3697390993773828), (1.3427726323007603, -1.7616141110472583), 
 (0.5022889371913024, 4.88736204694349), (2.345381610723872, -2.2849852099748915)]

Write to formatted:

with open("n.txt","w") as w:
    # w.write(f"{len(tupledata)}\n")  # uncomment for line number on top
    for t in tupledata:                 
        w.write("{:.3f},{:.3f}\n".format(*t)) 

        # for python 3.6 up you can alternatively use string literal interpolation:
        # see    https://www.python.org/dev/peps/pep-0498/

        # w.write(f"{t[0]:.3f},{t[1]:.3f}\n")

with open("n.txt","r") as r:
    print(r.read())

Output in file:

1.425,1.917
0.795,0.683
-0.751,3.579
-1.018,2.261
-2.952,4.018
1.820,-2.049
-2.273,1.370
1.343,-1.762
0.502,4.887
2.345,-2.285

See proper name for python * operator? for what *t does. Hint: print(*[1,2,3]) == print(1,2,3)


Patrick Artner
  • 50,409
  • 9
  • 43
  • 69
  • I am new to Python so apologise for silly questions. I understand that * unpacks my tuples out of the list. Then whenever they are stored in the file, are they strings? If so, how were they converted? Second question is: in `w.write(f"{len(tupledata)}\n")`, what is the "f"? – spfortray Nov 15 '18 at 08:15
  • 1
    @spfortray `f"{3.14159:^12}"` its the "new" string interpolation method for formatting values - it is similar in syntax to the older `"... {:^12}".format(3.14159)` syntax - but more sugary (shorter) - its in since python 3.6 - see https://www.python.org/dev/peps/pep-0498/ . The content of your lists are floats - you simply write them formatted into the file - no need to create a list of string representation beforehand. – Patrick Artner Nov 15 '18 at 08:18
  • Thanks a lot for the reference on "f", I love how short and elegant it looks compared to the old .format(). I think I understand the writing part too now - I was confused since when I learned Python I was told that I could only write a string in a file, and a number would give an error, therefore I didn't realise I could write floats in a file without converting them to strings. – spfortray Nov 15 '18 at 08:33
  • 1
    @spfortray `file.write()` only takes strings. The `f"....."` converts your floats into a string. You can do smth like: `pi_as_string = f" my pi-pie {3,141} "` # to convert a (inlined, could use a variable here as well) float (3.141) into a string – Patrick Artner Nov 15 '18 at 08:37
  • I see! In the older version it was the .format(*t) that converted the coordinates into strings. Now `f"{t[0]:.3f},{t[1]:.3f}\n"` did the same, like with length. Brilliant solution, thank you explaining all this to me. – spfortray Nov 15 '18 at 08:49
2

You are mixing several things. There is no need to add the number in the list,nor you need to create an intermediary string list.Also the formatting is easier like this:

with open('points.txt', 'w') as f:
    f.write(str(len(points)))
    for x, y in points:
        f.write(f"{x:.2f}, {y:.2f}\n")
Netwave
  • 40,134
  • 6
  • 50
  • 93
1

Use tuple unpacking when constructing your lines:

lines = ["{:.3f}, {:.3f}\n".format(*point) for point in points]

That way you already have a list of strings you can easily write to a file. No need to convert them to float again, just to cast them to string again.

with open('points.txt', 'w') as f:
    f.writelines(lines)
Christian König
  • 3,437
  • 16
  • 28
  • This is great! Except now when I use `writelines` I can't have a newline and it just stores `lines` in a row. Is there a way to fix this (I am aware that `writelines` only takes one argument, but thought something similar to when I converted to string inside the `writelines()` could do)? – spfortray Nov 15 '18 at 08:20
  • 1
    Sorry! Updated the format string to include the newline. – Christian König Nov 15 '18 at 08:23