0

How can I convert a CSV file of names with 3 pieces of data (in adjacent columns) into variables of a created class that takes the adjacent pieces of data as arguments?

The code below appears to create the objects but the list is of object locations and not the names as I would prefer.

Can anyone help?

class Teams(object):
  def __init__(self, TeamName, FT, FG, Three):
    self.TeamName = TeamName
    self.FT = FT
    self.FG = FG
    self.Three = Three

North_Carolina = Teams("North Carolina", .643,.458,.371)

print North_Carolina.TeamName, North_Carolina.FT, North_Carolina.FG,North_Carolina.Three
## works fine but manually typed data

def getData(fname):  ##fname is a csv file with 4 columns and 348 rows. One header row.
    Data = open(fname, 'r')
    TeamList = []
    #print Data.read() ## prints a list with comma separated values and line return
    for line in Data:
      info = line.split(",") ##info is a list of lists of Data with Team,FT,FG,3P
      name = info[0]
      FT = info[1]
      FG = info[2]
      Three = info[3]
      newTeam = (name, FT, FG, Three)
      newTeam2 = Teams(newTeam[0],newTeam[1],newTeam[2],newTeam[3]) ##newTeam2 is an object. 
      TeamList.append(newTeam2)
    print TeamList[1].TeamName ##prints North Carolina list[0] print header
    print TeamList #prints list of object locations but not objects themselves

getData("Stats 01-04-2013.csv")

First print statement prints:

North Carolina

Second print statement prints:

[<__main__.Teams object at 0x02953230>, <__main__.Teams object at 0x029532B0>, etc etc
12
  • 1
    Is there a reason not to use the python csv module ? Commented Jan 4, 2013 at 23:28
  • @HaraldScheirich: In this case, it's not absolutely required, since it's a fairly simple CSV. But yeah, better use the library. Commented Jan 4, 2013 at 23:30
  • I'm not exactly clear what you want print Teamlist to print. Do you want a comma-separated list of all of the team names, or a newline-separated list of (a comma-separated list of all of the team properties) for each team, or …? If I've guessed wrong in my answer, please clarify exactly what your expected output is. Commented Jan 4, 2013 at 23:41
  • The Teamlist print statement is just to check my work. I want these names (variables of team class) to be called later in the program and each piece of data is used in calculations. Commented Jan 4, 2013 at 23:46
  • For example, there is a later function simGame(teamA, Teamb) that takes the shooting percentages FT, FG and Three of the given team and chooses whether the team makes or misses the shot based on the given percentage. Like a coin flip but weighted Commented Jan 4, 2013 at 23:47

4 Answers 4

1

I'd use the csv module to do something like this, largely based on the examples in the module documentation. Tweaks may be needed, this is just off the top of my head, but I always use the relevant when working with these types of files.

import csv

with open('teams.csv', 'rb') as csvfile:
    teamreader = csv.reader(csvfile)
    for row in teamreader:
        newTeam = Teams(row[0], row[1], row[2], row[3])
        teamList.append(newTeam)
Sign up to request clarification or add additional context in comments.

2 Comments

Interesting! I think I will take this idea and combine it with the Dict idea and see if I can get some elegant looking code.
+1. Not a direct answer to the OP's problem, but it will probably help make his code more readable and easier to maintain.
0

The object locations are how the objects are printed out. Strictly speaking, calling str or repr on a list calls repr on each element on the list. By default, repr(team) on a Teams object will return something like <main.Teams object at 0x02953230>. You can override that by defining a __repr__ method on the Teams class. However, this doesn't sound like what you want here.

If TeamList is a list of Teams objects, and you want to turn it into a list of their TeamName members, you just use a list comprehension to convert it:

TeamNameList = [team.TeamName for team in TeamList]

Note that this still isn't going to be exactly you want to print out:

>>> print TeamNameList
['North Carolina', 'South Carolina', 'West Carolina', 'East Carolina', 'Underground Carolina', 'Cloud-level Carolina', 'Past Carolina', 'Future Carolina'] # those are all states, right?

You probably want something like this:

>>> print ', '.join(TeamNameList)
North Carolina, South Carolina, West Carolina, East Carolina, Underground Carolina, Cloud-level Carolina, Past Carolina, Future Carolina

From your comments:

there is a later function simGame(teamA, Teamb)

I see the instances, however I am not sure how to call them in the second function. I had planned on calling them by name. In the example I gave, North_carolina can be passed to the later (not shown function) and calculations can be run on the data

I think I understand what you want here. You want to be able to simulate a game between North Carolina and South Carolina, when all you have is the TeamList.

To do that, you probably want to create a dict, mapping the names to the Teams objects, like this:

TeamDict = {team.TeamName: team for team in TeamList}

Now, you can do things like this:

simGame(TeamDict['North Carolina'], TeamDict['South Carolina'])

Now, simGame is going to get the North Carolina and South Carolina instances of the Teams class as its teamA and teamB arguments, so it can do things like:

def simGame(teamA, teamB):
    scoreA = int(teamA.FT * 1 * 20) + int(teamA.FG * 2 * 40) + int(teamA.Three * 3 * 10)
    scoreB = int(teamB.FT * 1 * 20) + int(teamB.FG * 2 * 40) + int(teamB.Three * 3 * 10)
    if scoreA > scoreB:
        print 'Home team {} beats visitor {} by {}!'.format(teamA.TeamName,
                                                            teamB.TeamName,
                                                            scoreA - scoreB)
    else:
        print 'Visitors {} win by {} over {} at home!'.format(teamB.TeamName,
                                                              scoreB - scoreA,
                                                              teamA.TeamName)

Is that what you want?

Some additional comments:

You can also do the same thing as the list comprehension using map, which avoids having to write team twice, but it also means you can't use normal expression syntax:

TeamNameList = map(operator.attrgetter('TeamName'), TeamList)

Or you can use map together with lambda to get back the expression syntax back… and the repeat of team:

TeamNameList = map(lambda team: team.teamName, TeamList)

But for cases like this, the list comprehension is generally considered the pythonic way to do it. (Also, it doesn't change if you go to Python 3, whereas map changes from a list to an iterator, which means print TeamNameList will give you something like <builtins.map at 0x108022350>… But ', '.join(TeamNameList) will still work.)

As a side note, in standard (PEP 8) Python style, usually only classes are in TitleCase like this, and variables and attributes are in lower_case. If you really like CamelCase, you can get away with lowerFirstCamelCase, but using TitleCase will throw off people reading your code, who will immediately try to figure out where the TeamName class is defined. Also see the comments from bvukelic.

As another side note, you seem to be doing a lot of repeated code, and I'm not sure why:

  name = info[0]
  FT = info[1]
  FG = info[2]
  Three = info[3]
  newTeam = (name, FT, FG, Three)

You're just copying info to newTeam; why add all those intermediate variables that never get used again?

  newTeam2 = Teams(newTeam[0],newTeam[1],newTeam[2],newTeam[3])

You could replace all of that with just:

  newTeam2 = Teams(info[0],info[1],newTeam[2],newTeam[3])

Or even:

  newTeam2 = Teams(*info)

If you have a need for the separate variables somewhere that you haven't shown us, that's fine, but you still can't possibly need newTeam; just do this:

  newTeam2 = Teams(name, FT, FG, Three)

14 Comments

Speaking of PEP8 (and also according to common agreement in different languages out there), app-caps names should also not be used for anything else than constants.
@bvukelic: Good point. With two-letter abbreviated names (which themselves should be used sparingly, of course…), it doesn't seem too unreasonable to treat FT as abbreviated CamelCase instead of as ALL_CAPS… But yeah, I still wouldn't ever name anything FT, even if I could find a way to rationalize it away.
@abarnet: Wouldn't even do that with classes. :) I would always expand if it's too short. Also, using slightly longer names can yield self-documenting code, which is a nice thing.
This is really good stuff. The reason for these "problems" you've identified is that I'm a novice - self trained programmer so I am learning proper techniques as I go. Thanks for the help. Still reading your previous comments.
Oh and I'm new to reading code so I space things out. It's repetitive to a veteran programmer but it's the only way I prevent myself from creating syntax errors.
|
0

Just to cover another interpretation of the question:

class Teams(object):
    def __init__(self, TeamName, FT, FG, Three):
        self.TeamName = TeamName
        self.FT = FT
        self.FG = FG
        self.Three = Three

    def __str__(self):
        return str(self.TeamName)

    def __repr__(self):
        return self.__unicode__()

Whenever you print the object, it will be shown as TeamName

11 Comments

True, but when you print a list of the objects, as the OP does, this won't help—as I explained in my answer, str or repr on a list calls repr on the elements, not str. And the same goes for unicode, of course—but with the additional problem that even print team won't call unicode on it; it calls str. So this will only have an effect if you explicitly do unicode(team) somewhere.
Also, now that I think about it, you're returning a str from __unicode__, which is quite misleading.
You've only corrected the list problem. There's still not __str__. And __unicode__ still returns a str object, not a unicode.
OK, now it works… but why would you do '%s' % self.TeamName instead of just str(self.TeamName)?
@abarnert I don't know. Force of habit? Is there a significant difference?
|
0

How class instances get displayed is controlled by giving them custom __str__() and/or __repr__() conversion methods. Here's one way your code could be written to do that and also make it follow many of the recommendations in the PEP 8 Style Guide. You may also be able improve or simplify it even more by using the csv module to read the file, as already suggested by others, but I'll focus mostly on the code shown in your question.

class Team(object):
    def __init__(self, team_name, free_throws, field_goals, three_pointers):
        self.team_name = team_name
        self.free_throws = free_throws
        self.field_goals = field_goals
        self.three_pointers = three_pointers

    def __repr__(self):
        return '{}({!r}, {}, {}, {})'.format(self.__class__.__name__,
                                             self.team_name, self.free_throws,
                                             self.field_goals, self.three_pointers)

def get_data(fname):  # fname is a csv file with 4 columns and one header row
    teams = []
    with open(fname, 'rt') as csv_file:
        csv_file.next()  # skip header row
        for line in csv_file:
            info = line.strip().split(",")
            teams.append(Team(*info))  # create a Team instance and add it to list
    return teams

print get_data("Stats 01-04-2013.csv")

Sample output:

[Team('North Carolina',  .643,  .458,  .371), Team('Michigan',  .543,  .358,  .271)]

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.