如何将 csv 文件的内容读入一个类,每个 csv 行作为一个类实例
Posted
技术标签:
【中文标题】如何将 csv 文件的内容读入一个类,每个 csv 行作为一个类实例【英文标题】:How to read the contents of a csv file into a class with each csv row as a class instance 【发布时间】:2018-05-06 20:01:36 【问题描述】:我是一名 Python 新手,几天来一直在努力完成课堂作业。我有一个 csv 文件,其中包含如下数据:
id,latitude,longitude,city,label,yr1970,yr1975,yr1980,yr1985,yr1990,yr1995,yr2000,yr2005
1,35.6832085,139.8089447,Tokyo,Tokyo,23.3,26.61,28.55,30.3,32.53,33.59,34.45,35.62
此文件中有大约 40 行,每行包含与世界城市相关的数据。如您所见,第一行是标题。我应该在 Python 中创建一个类并将 csv 文件读入该类,其中每一行都成为该类的一个实例。然后我将类实例存储在一个列表中。我已经能够创建一个存储所有数据的实例,但我似乎无法为每一行创建一个实例(而且我显然不想手动创建)。
这是我目前所得到的:
import csv
Cities = []
with open('filepath','rb') as f:
cityList = csv.reader(f)
for row in cityList:
if row != 'label':
for row in cityList:
citysName = row[3]
class City:
def __init__(self, cityName=row[3], Label=row[4], Lat=row[1],
Lon=row[2], yr1970=row[5], yr1975=row[6], yr1980=row[7],
yr1985=row[8], yr1990=row[9], yr1995=row[10], yr2000=row[11],
yr2005=row[12], yr2010=row[13]):
self.cityName = cityName
self.label = Label
self.lat = Lat
self.lon = Lon
self.yr1970 = yr1970
self.yr1975 = yr1975
self.yr1980 = yr1980
self.yr1985 = yr1985
self.yr1990 = yr1990
self.yr1995 = yr1995
self.yr2000 = yr2000
self.yr2005 = yr2005
self.yr2010 = yr2010
citysName = City()
Cities.append(citysName.cityName)
Cities.append(citysName.label)
Cities.append(citysName.lat)
Cities.append(citysName.lon)
Cities.append(citysName.yr1970)
Cities.append(citysName.yr1975)
Cities.append(citysName.yr1980)
Cities.append(citysName.yr1985)
Cities.append(citysName.yr1990)
Cities.append(citysName.yr1995)
Cities.append(citysName.yr2000)
Cities.append(citysName.yr2005)
Cities.append(citysName.yr2010)
print Cities
再说一次,我对 Python 很陌生(和一般的编码),我意识到这段代码不好,但我很难找到将 csv 文件读入 Python 类的技巧。
【问题讨论】:
您应该在循环之前声明一次类类型。然后创建实例,将行元素作为参数传递。 您的数据不匹配。你有 9 年,只有 8 个数据值。 【参考方案1】:你可以试试这个:
import csv
class City:
def __init__(self, row, header):
self.__dict__ = dict(zip(header, row))
data = list(csv.reader(open('file.csv')))
instances = [City(i, data[0]) for i in data[1:]]
但是,由于您提到有很多行,您可能希望为每个城市创建一个 id,作为您在列表中的字符串表示形式:
import csv
class City:
def __init__(self, row, header, the_id):
self.__dict__ = dict(zip(header, row))
self.the_id = the_id
def __repr__(self):
return self.the_id
data = list(csv.reader(open('file.csv')))
instances = [City(a, data[0], "city_".format(i+1)) for i, a in enumerate(data[1:])]
您的输出将是这样的列表:
[city_1, city_2, city_3...]
任何属性都可以这样调用:
instances[1].latitude
关于您最近的评论,要按城市名称访问城市属性,您可以稍微重组instances
:
instances = a[3]:City(a, data[0], "city_".format(i+1)) for i, a in enumerate(data[1:])
【讨论】:
这似乎有效,谢谢!您知道是否有任何方法可以通过城市名称调用属性? (即,instances['Tokyo'].latitude) 取出了 'City' 之前的 '[',效果很好,非常感谢!【参考方案2】:您可以使用csv.DictReader
来做到这一点,它将每一行作为字典返回。它还允许您根据 csv 文件的第一行或标题行中定义的字段名轻松定义类,而不是将它们硬编码到您的程序中。
您仍然可以使用“点”语法(例如 city.latitude
、city.label
等)引用任何记录的字段。
以下是 Python 2 代码。对于 Python 3,使用以下命令打开文件:
with open('cities.csv', 'r', newline='') as f:
而不是显示的内容。
import csv
with open('cities.csv', 'rb') as f:
reader = csv.DictReader(f)
fieldnames = reader.fieldnames
class City:
def __init__(self, **fields):
self.__dict__.update(**fields)
def __repr__(self): # Added to make printing instances show their contents.
fields = ', '.join(('=!r'.format(fieldname, getattr(self, fieldname))
for fieldname in fieldnames))
return('()'.format(self.__class__.__name__, fields))
Cities = [City(**row) for row in reader]
print(Cities)
示例输入文件的示例输出:
[City(id='1', latitude='35.6832085', longitude='139.8089447', city='Tokyo', label='Tokyo', yr1970='23.3', yr1975='26.61', yr1980='28.55', yr1985='30.3', yr1990='32.53', yr1995='33.59', yr2000='34.45', yr2005='35.62')]
【讨论】:
【参考方案3】:如果您的数据只是不可变的记录,请使用namedtuple
:
>>> from collections import namedtuple
>>> City = namedtuple('City', 'lat lon cityName label '
... 'yr1970 yr1975 yr1980 yr1985 yr1990 yr1995 yr2000 yr2005 yr2010')
您可以对不需要第一个值的行进行切片,然后使用*
解包:
>>> row = ['1', '35.6832085', '139.8089447', 'Tokyo', 'Tokyo',
... '23.3', '26.61', '28.55', '30.3', '32.53', '33.59', '34.45', '35.62', '35.7']
>>> city = City(*row[1:])
>>> city
City(lat='35.6832085', lon='139.8089447', cityName='Tokyo', label='Tokyo',
yr1970='23.3', yr1975='26.61', yr1980='28.55', yr1985='30.3', yr1990='32.53',
yr1995='33.59', yr2000='34.45', yr2005='35.62', yr2010='35.7')
您只需要将这个对象添加到您的城市列表中,而不是每个属性:
>>> cities.append(city)
将它与过滤掉标签行的列表理解放在一起:
import csv
from collections import namedtuple
City = namedtuple('City',
'lat lon cityName label '
'yr1970 yr1975 yr1980 yr1985 yr1990 yr1995 yr2000 yr2005 yr2010')
with open('filepath') as f:
cities = [City(*row[1:]) for row in csv.reader(f)
if row[0] != 'label']
【讨论】:
【参考方案4】:import csv
class City:
def __init__(self, **kwargs):
self.id = kwargs.get('id')
self.latitude = kwargs.get('latitude')
self.longitude = kwargs.get('longitude')
self.city = kwargs.get('city')
self.label = kwargs.get('label')
self.year_1970 = kwargs.get('yr1970')
self.year_1975 = kwargs.get('yr1975')
self.year_1980 = kwargs.get('yr1980')
self.year_1985 = kwargs.get('yr1985')
self.year_1990 = kwargs.get('yr1990')
self.year_1995 = kwargs.get('yr1995')
self.year_2000 = kwargs.get('yr2000')
self.year_2005 = kwargs.get('yr2005')
def __str__(self):
return self.label
if __name__ == '__main__':
with open('filepath', 'r') as csvfile:
reader = csv.DictReader(csvfile)
for row in reader:
city = City(**row)
print(city)
【讨论】:
【参考方案5】:一些帮助你清理代码的技巧:
而不是这个:
self.yr1970
定义一个列表来跟踪年份及其值:
tokyo_years =
1970: 23.3,
1975: 26.61,
# ...
现在将这个结构与每个城市配对:
cities = [
'city': 'Tokyo', 'years': tokyo_years ,
'city': 'Vancouver', 'years': vancouver_years ,
# ...
]
不要嵌套得那么深。此外,以下内容真的很奇怪:
for row in cityList:
if row != 'label':
for row in cityList:
您正在循环某些东西,然后在循环时再次循环它......!
类属于***。这意味着class
前面应该有0个空格。
class City:
应该是:
class City:
我提到这一切的原因是因为试图用混乱的代码做任何进一步的事情只会导致代码更混乱。 :) 尝试通过以下方式改进您当前的代码:
-
使用数据结构(列表、字典)。
将嵌套代码的级别限制为最多 2 个。 (考虑使用函数来帮助您。)
将课程置于顶层。
【讨论】:
以上是关于如何将 csv 文件的内容读入一个类,每个 csv 行作为一个类实例的主要内容,如果未能解决你的问题,请参考以下文章
如何将压缩(gz)CSV文件读入dask Dataframe?