我编写了一个程序,它读取了几个.csv文件(它们不大,每个都有几千行),我做了一些数据清理和争吵,这是每个.csv文件看起来的最终结构(假数据仅供参考。)
import pandas as pd
data = [[112233, 'Rob', 99], [445566, 'John', 88]]
managers = pd.DataFrame(data)
managers.columns = ['ManagerId', 'ManagerName', 'ShopId']
print managers
ManagerId ManagerName ShopId
0 112233 Rob 99
1 445566 John 88
data = [[99, 'Shop1'], [88, 'Shop2']]
shops = pd.DataFrame(data)
shops.columns = ['ShopId', 'ShopName']
print shops
ShopId ShopName
0 99 Shop1
1 88 Shop2
data = [[99, 2000, 3000, 4000], [88, 2500, 3500, 4500]]
sales = pd.DataFrame(data)
sales.columns = ['ShopId', 'Year2010', 'Year2011', 'Year2012']
print sales
ShopId Year2010 Year2011 Year2012
0 99 2000 3000 4000
1 88 2500 3500 4500
然后我使用xlsxwriter
和reportlab
Python包在迭代数据框时创建自定义Excel工作表和.pdf报告。一切看起来都很棒,所有命名的包都能很好地完成它们的工作。
我担心的是我觉得我的代码很难维护,因为我需要在多次调用中多次访问相同的数据帧行。
假设我需要获得负责2010年销售额超过1500的商店的经理名称。我的代码充满了这种电话:
managers[managers['ShopId'].isin(
sales[sales['Year2010'] > 1500]['ShopId'])]['ManagerName'].values
>>> array(['Rob', 'John'], dtype=object)
我认为在阅读这行代码时很难看到发生了什么。我可以创建多个中间变量,但这会添加多行代码。
牺牲数据库规范化意识形态并将所有部分合并到一个数据框中以获得更易于维护的代码有多常见?显然有一个单一数据框的缺点,因为在尝试合并稍后可能需要的其他数据帧时它可能会变得混乱。合并它们当然会导致数据冗余,因为同一个经理可以分配到多个商店。
df = managers.merge(sales, how='left', on='ShopId').
merge(shops, how='left', on='ShopId')
print df
ManagerId ManagerName ShopId Year2010 Year2011 Year2012 ShopName
0 112233 Rob 99 2000 3000 4000 Shop1
1 445566 John 88 2500 3500 4500 Shop2
至少此调用会变小:
df[df['Year2010'] > 1500]['ManagerName'].values
>>> array(['Rob', 'John'], dtype=object)
也许熊猫是这种工作的错误工具?
办公室里的C#开发人员对我皱眉并告诉我使用这些类,但后来我会有一堆像get_manager_sales(managerid)
这样的方法。迭代用于报告的类实例也听起来很麻烦,因为我需要实现一些排序和索引(我使用pandas
免费获得)。
字典可行,但它也很难修改现有数据,进行合并等。语法也没有好转。
data_dict = df.to_dict('records')
[{'ManagerId': 112233L,
'ManagerName': 'Rob',
'ShopId': 99L,
'ShopName': 'Shop1',
'Year2010': 2000L,
'Year2011': 3000L,
'Year2012': 4000L},
{'ManagerId': 445566L,
'ManagerName': 'John',
'ShopId': 88L,
'ShopName': 'Shop2',
'Year2010': 2500L,
'Year2011': 3500L,
'Year2012': 4500L}]
获取经理名称,负责2010年销售额超过1500的商店。
[row['ManagerName'] for row in data_dict if row['Year2010'] > 1500]
>>> ['Rob', 'John']
在我操作的数据的这种特殊情况下,我是否应该使用pandas
一直走,或者是否有另一种方法来编写更清晰的代码,同时利用pandas
的强大功能吗
答案 0 :(得分:3)
我会选择Pandas,因为它更快,拥有优秀且极其丰富的API,源代码看起来更干净,更好等等。
BTW以下行可以轻松改写:
managers[managers['ShopId'].isin(sales[sales['Year2010'] > 1500]['ShopId'])]['ManagerName'].values
为:
ShopIds = sales.ix[sales['Year2010'] > 1500, 'ShopId']
managers.query('ShopId in @ShopIds')['ManagerName'].values
IMO很容易阅读和理解
PS您可能还希望将数据存储在SQL
数据库中并使用SQL或将其存储在HDF存储中并使用where
参数 - 在这两种情况下,您都可以从索引中受益“搜索“列
答案 1 :(得分:3)
创建对数据帧进行操作的类不是一个好主意,因为它会隐藏您正在使用数据框的事实,并为非常糟糕的决策开辟道路(例如使用{{迭代数据框) 1}}循环)。
解决方案1:对数据进行非规范化。 您不必以正常形式保存数据。当您必须在整个数据库中保持条目一致时,可以使用普通表单。这不是数据库,您不进行常量插入,更新和删除。因此,只需对其进行反规范化,并使用一个大型数据帧,因为它显然更方便,更适合您的需求。
解决方案2:使用数据库。 您可以将数据转储到SQLite数据库中(pandas具有内置函数),并对其执行各种疯狂查询。在我个人的观点中,SQL查询比您发布的内容更具可读性。 如果您定期进行此类分析,并且数据结构保持不变,则这可能是一种可取的解决方案。您可以将数据转储到数据库中,然后使用SQLAlchemy来处理它。
解决方案3.创建您自己的数据框。
您可以从for
继承并向其添加自定义方法。但是,您需要深入了解pandas.DataFrame
的内容,以了解如何实现这些方法。这样,您就可以创建访问数据框某些部分的自定义方法。
除非您非常了解大熊猫,否则我会选择解决方案1或2.如果您需要更多灵活性,并且数据操作每次都不同,请使用1.如果您需要每次执行大致相同的分析,使用2(特别是如果您的数据分析代码是更大的应用程序的一部分)。
另外,我不明白为什么“添加更多代码行”是不好的。通过将巨大的单行分解为多个表达式,您不会增加实际复杂性,并且会降低感知复杂性。也许您需要做的只是重构代码,并将一些操作打包成可重用的函数?