我有一个可变列宽的csv文件,其中每一行都是日期,区域和在该日期/区域观察到的一组ID。
数据如下所示:
12/01/2017,Region1,BMW_123,TESLA_332,TESLA_2002
11/07/2017,Region2,TESLA_332,BMW_123,TESLA_2002,TESLA_99812
11/19/2017,Region2,BMW_123,TESLA_31
10/23/2017,Region1,BMW_4,TESLA_3,TESLA_90
11/02/2017,Region2,TESLA_28,BMW_56,TESLA_22,TESLA_821
10/14/2017,Region2,BMW_1,BMW_8,BMW_2,TESLA_3,TESLA_4,TESLA_99,TESLA_81,TESLA_56
我想:
(1)拆分所有ID并保留品牌,因此“TESLA_12345”将成为“TESLA”。这部分没问题。
(2)按月份区域汇总,以便我们知道每个区域元组的每个品牌(例如特斯拉或宝马)的失败次数。
输出应该看起来像这样(格式不是非常重要 - 它必须清晰易读):
month region BMW TESLA
10 Region1 1 2
12 Region1 1 2
10 Region2 3 5
11 Region2 3 7
这样做的一种自然方式似乎是逐行读取CSV并更新嵌套字典中每个品牌的计数(即,有一个区域字典包含2个区域,每个区域包含保留的月份序列像{宝马:2,TESLA:1}那样的计数。然而,我正在努力更新嵌套的dicts,我想知道是否有一个更简单的解决方案(或者如果Pandas可以毫不费力地做到这一点等)。
(注意:显然,月份可以从日期中提取:
datetime.strptime(mydate, "%m/%d/%Y").strftime("%m")
)
答案 0 :(得分:4)
这是一种方法。我不会称之为漂亮,但它完成了工作。
第一个问题是每行中的字段数不同。您可以一次在文件中读取一行,并将其存储在列表data
中。您还可以在执行此操作时修剪汽车ID:
import pandas as pd
# assuming CSV is named test.csv
f = open("test.csv", "r")
data = []
for i, line in enumerate(f.readlines()):
splitted = line.split(",")
just_brand = [x.split("_")[0] for x in splitted]
data.append(just_brand)
现在我们已将文件读入Python数据结构,我们可以重新排序文件行,使得具有最多字段数的条目位于顶部。这对Pandas来说很有用,因为它可以比额外的列更好地处理丢失的列。如果我们从最大数量的列开始,将更好地处理更短的后续行。
df = pd.DataFrame(sorted(data, key=lambda row: len(row), reverse=True))
df
0 1 2 3 4 5 6 7 8 9
0 10/14/2017 Region2 BMW BMW BMW TESLA TESLA TESLA TESLA TESLA
1 11/07/2017 Region2 TESLA BMW TESLA TESLA None None None None
2 11/02/2017 Region2 TESLA BMW TESLA TESLA None None None None
3 12/01/2017 Region1 BMW TESLA TESLA None None None None None
4 10/23/2017 Region1 BMW TESLA TESLA None None None None None
5 11/19/2017 Region2 BMW TESLA None None None None None None
从这里开始,只需要对事物进行命名,组织和格式化。
df = (df.set_index([0,1])
.stack()
.reset_index(level=1)
.rename(columns={1:"region",0:"make"})
.reset_index(level=1, drop=True))
df = (df.groupby([pd.to_datetime(df.index).month,"region","make"])
.make.count()
.unstack()
.reset_index()
.rename(columns={0:"month"}))
df.columns.name = ""
df
region BMW TESLA month
0 Region1 1 2 10
1 Region2 3 5 10
2 Region2 3 7 11
3 Region1 1 2 12
(替代,不那么体操)
# get TESLA, BMW counts for each row
cts = df.iloc[:,2:].apply(lambda x: x.value_counts(), axis=1)
# merge with date, region
df2 = pd.concat([df.iloc[:, :2], cts], axis=1)
# groupby and sum
(df2.groupby([pd.to_datetime(df[0]).dt.month,1])
.sum()
.reset_index()
.rename(columns={0:"month",1:"region"}))