Tell me more ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

For some reason I can't log into the same account on my home computer as my work computer.
I was able to get Bo10's code to work, but not abernert's and I would really like to understand why.

Here is my updates to abernert's code:

    import csv
    import sys
    import json
    import urllib2

    j = urllib2.urlopen('https://citibikenyc.com/stations/json')
    js = json.load(j)

    citi = js['stationBeanList']

    columns = ('stationName', 'totalDocks', 'availableDocks', 
               'latitude', 'longitude', 'availableBikes')
    stations = (operator.itemgetter(columns)(station) for station in citi)

    with open('output.csv', 'w') as csv_file:
        csv_writer = csv.writer(csv_file)
        csv_file.writerows(stations)

I thought adding this line `csv_writer = csv.writer(csv_file)` would fix the object has no attirbute error, but I am still getting it.  This is the actual error:

Andrews-MacBook:coding Andrew$ python citibike1.py
Traceback (most recent call last):
  File "citibike1.py", line 17, in <module>
    csv_file.writerows(stations)
AttributeError: 'file' object has no attribute 'writerows'

So now I have the changed the code to this and the output is just repeating the names of the columns 322 times. I changed it on line 14 because i was getting this error:

Traceback (most recent call last):
  File "citibike1.py", line 17, in <module>
    csv_writer.writerows(stations)
  File "citibike1.py", line 13, in <genexpr>
    stations = (operator.itemgetter(columns)(station) for station in citi)
NameError: global name 'operator' is not defined: 

import csv
import sys
import json
import urllib2
import operator

j = urllib2.urlopen('https://citibikenyc.com/stations/json')
js = json.load(j)

citi = js['stationBeanList']

columns = ('stationName', 'totalDocks', 'availableDocks', 
           'latitude', 'longitude', 'availableBikes')
stations = (operator.itemgetter(0,1,2,3,4,5)(columns) for station in citi)

with open('output.csv', 'w') as csv_file:
    csv_writer = csv.writer(csv_file)
    csv_writer.writerows(stations)
share|improve this question
As a side note: Naming a variable csv is a bad idea, because it hides the csv module. That's especially true in this case, when you want to use the csv module—you can't do csv.writer after you've done csv = open(…). (Also, it's both simpler and more robust to use a with statement instead of explicit open and close.) – abarnert Jun 27 at 0:09
thank you! could you explain to me how i would use a with statement in this scenario? – user1887261 Jun 27 at 0:12
Just do with open('output.csv', 'w') as csv_file:, then put the code that uses csv_file indented under the with, then you don't need to call close. – abarnert Jun 27 at 0:26
For more info, look at the very end of the section Methods of File Objects in the tutorial and PEP 343. (There really should be better intro material for with statements than there is!) – abarnert Jun 27 at 0:29
One last thing: Your title says "two columns", your question detail says "6 separate columns", and your code builds 322 columns. Which one do you actually want? – abarnert Jun 27 at 0:34
show 4 more comments

2 Answers

The problem is that you're not using the csv module, you're using the pickle module, and this is what pickle output looks like.

To fix it:

csvfile = open('output.csv', 'w')
csv.writer(csvfile).writerows(stationList)
csvfile.close()

Note that you're going out of your way to build a transposed table, with 6 lists of 322 lists, not 322 lists of 6 lists. So, you're going to get 6 rows of 322 columns each. If you want the opposite, just don't do that:

stationList = []
for f in citi:
    stationList.append((f['stationName'],
                        f['totalDocks'],
                        f['availableDocks'],
                        f['latitude'],
                        f['longitude'],
                        f['availableBikes']))

Or, more briefly:

stationlist = map(operator.itemgetter('stationName', 'totalDocks', 'availableDocks',
                                      'latitude', 'longitude', 'availableBikes'),
                  citi)

However, instead of building up a huge list, you may want to consider writing the rows one at a time.

You can do that by putting csv.writerow calls into the middle of the for loop.

But you can also do that just by using itertools.imap or a generator expression instead of map or a list comprehension. That will make stationlist into an iterable that creates new values as needed, instead of creating them all at once.


Putting that all together, here's how I'd write your program:

import csv
import sys
import json
import urllib2

j = urllib2.urlopen('https://citibikenyc.com/stations/json')
js = json.load(j)

citi = js['stationBeanList']

columns = ('stationName', 'totalDocks', 'availableDocks', 
           'latitude', 'longitude', 'availableBikes')
stations = (operator.itemgetter(columns)(station) for station in citi)

with open('output.csv', 'w') as csv_file:
    csv.writer(csv_file).writerows(stations)
share|improve this answer
Hmmm. I am getting this error: Traceback (most recent call last): File "citibike.py", line 23, in <module> csv.writer(csvfile).writerows(csv) TypeError: writerows() argument must be iterable – user1887261 Jun 27 at 0:10
@user2092166: Sorry, you should be writing stationList, not csv. (But you really need to understand the code well enough to figure that out for yourself, not just blindly copy and paste it.) – abarnert Jun 27 at 0:16
hi, i am getting errors on both of your edited scenarios – user1887261 Jun 27 at 0:17
i'm trying! sadly, i did not catch that... – user1887261 Jun 27 at 0:18
Just saying "i am getting errors" is not useful. You have to explain exactly what you did, and exactly what errors you got, or nobody can help you debug the problem. – abarnert Jun 27 at 0:24
show 3 more comments

As abarnert mentions, you're not actually using the csv module that you've imported.

Also, your logic for storing the columns might actually be transposed. I think you might want to do this instead (edited to fix the tuple/list confusion):

import csv
import json
import urllib2

j = urllib2.urlopen('https://citibikenyc.com/stations/json')
js = json.load(j)

citi = js['stationBeanList']

columns = ["stationName", "totalDocks", "availableDocks", "latitude", "longitude", "availableBikes"]
station_list = [[f[s] for s in columns] for f in citi]

with open("output.csv", 'w') as outfile:
  csv_writer = csv.writer(outfile)
  csv_writer.writerows(station_list)
share|improve this answer
i am getting this error: Traceback (most recent call last): File "citibike1.py", line 15, in <module> _ = csv_writer.writerows(station_list) _csv.Error: sequence expected – user1887261 Jun 27 at 0:15
IIRC, in 2.x, writerows requires an iterable of sequences, not just an iterable of iterables, so you need to change the internal genexp into a listcomp to make this work. – abarnert Jun 27 at 0:18
Also, why are you assigning the result of writerows (which is always None) to a variable? – abarnert Jun 27 at 0:18
Whoops, I meant to put brackets instead of parens. I couldn't remember whether writerows returned something, but I know writerow did, hence the assignment. – Bo102010 Jun 27 at 0:20
writerow also always returns None. (What could it return of any use, anyway?) Also, in Python, if something returns a value you don't need, you can and should just ignore it. Assigning it to a variable gets in the way of the GC (and the JIT optimizer, in PyPy/Jython/IronPython), and of every human reader. – abarnert Jun 27 at 0:22
show 8 more comments

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.