Python-对每行最后10个日期中的值进行分组



我正在尝试对每行前10个日期中的值进行分组。我有一个.csv文件,其中有列:日期、项目、销售额、合作伙伴,我需要这样的结果:日期、项、总额(销售额)(该特定项目销售额的最后10天),所以只对该项目的销售额进行分组。日期不是按顺序排列的(有些日期根本没有销售,所以我不能从日期中减去10天)。例如,

日期项目类型销售伙伴2014/01/01 A$100 P22014/01/01 B 60美元P12014/01/04 A 70 P22014/01/06 B$80 P22014/01/08 A$40 P32014/01/09 B第2页20美元。。。

对于上面的数据集,我需要得到这样的结果:

日期项目销售(最近10个日期内)2014/01/04 A 170美元2014/01/08 A$210

我需要为文件中的每一行自动生成这个
此外,我还需要获得最近10个日期的每个合作伙伴(只有4个合作伙伴)的计数:

日期项目P1 P2 P3 P42014/01/08 A 0 2 1 0

对于后者,我可能需要分组,然后转置。我想我需要使用Panda和/或num.py模块来进行这些分组,但我是Python的新手,还没有找到一个例子。

您不需要像Panda或numpy这样复杂的东西——这是一个使用csv模块的相当简单的流程。

你可以这样做:

  • 使用csv阅读器读取文件,这样就有了类似的数据

    [['2014/01/01', 'A', '$100', 'P2'], ['2014/01/01', 'B', '$60', 'P1'], ]

  • 对键入日期的数据进行排序

  • 使用itertools.groupby按日期分组

  • 使用切片表示法(print([1,2,3,4][:2]))获取10个日期的

  • 使用csv模块显示或写入新文件

如果你对这些步骤中的任何一个有问题,请回来发布一个新问题!

假设您读取了列表中的csv,l[0]是日期字段。你可以这样使用itertools

from itertools import groupby
# read csv file in this list
csv_list = [
    ['2014/01/01', '100'],
    ['2014/01/01', '200'],
    ['2014/01/04', '70'],
    ['2014/01/08', '40']
]
# make sure the list is sorted by the date in order for grouping to work ok
csv_list.sort(key=lambda i: i[0])
result = [(date, sum(values[1])) for date, values in groupby(csv_list, key=lambda i: i[0])]

result应包含一个元组列表,其中包含(日期,该日期中的sum_of_sales)。

这是分组的一部分。如果要解析实际datetime对象中的日期并进行比较,以便可以使用strptime按特定顺序打印值。

我想我理解你的问题。此解决方案需要一个日期,并找到该日期之前最后N次销售的总和。看看它:

import csv
from itertools import groupby, islice, ifilter
from datetime import datetime

def sum_sales(date=None, filename='my_data.csv', n_days=10, items=None):
    if date is None:
        date = datetime.today()
    with open(filename) as ifile:
        reader = csv.reader(ifile, skipinitialspace=True, delimiter=' ')
        # Skip the header
        next(reader)
        # Convenience functions to use later on
        item_date = lambda row: (row[1], datetime.strptime(row[0], '%Y/%m/%d'))
        if items is None:
            filter_by = lambda row: datetime.strptime(row[0], '%Y/%m/%d') <= date
        else:
            filter_by = lambda row: datetime.strptime(row[0], '%Y/%m/%d') <= date 
                and row[1] in items
        # Loop over groups of data, sorted by ItemType and data, grouped by
        # ItemType and filtered by filter_by
        for item, group in groupby(sorted(ifilter(
                filter_by, reader), key=item_date), lambda row: row[1]):
            partners = {'P1': 0, 'P2': 0, 'P3': 0, 'P4': 0}
            data = islice(group, n_days)
            sales = 0
            for row in data:
                sales += int(row[2].replace('$', ''))
                partners[row[3]] += 1
            print '{}t{}t{}'.format(date.date(), item, sales)
            print '{}t{}t{P1}t{P2}t{P3}t{P4}'.format(date.date(), item,
                                                          **partners)

假设您的示例输入保存在my_data.csv中,这将是输出:

>>> sum_sales()
2014-04-10  A   210
2014-04-10  A   0   2   1   0
2014-04-10  B   160
2014-04-10  B   1   4   1   0
>>> sum_sales(datetime(year=2014, month=1, day=4))
2014-01-04  A   170
2014-01-04  A   0   2   0   0
2014-01-04  B   60
2014-01-04  B   1   2   0   0
>>> sum_sales(datetime(year=2014, month=1, day=8), items=['A'])
2014-01-08  A   210
2014-01-08  A   0   2   1   0

您可能想将结果引导到一个新的csv文件,但这对您来说应该不是问题。

鉴于您是python的新手,我制定了一个不包含第三方实用程序的解决方案,以便您可以学习python。这涉及到首先将表条目分组到字符串和数字类型的条目的字典列表中我将把文件读取留给你

主文件中的一个空条目看起来像。

entries = [ {"Date":"", "ItemType":"", "Sales":int(), "Partner":""}, ... ]

我做了三个函数来满足你的需要。

  • filter_entries(entries, filter_key),返回key:基于条目的条目列表项,其中条目列表[filter_key]值完全相同
  • expand_entries(entries, expand_key)返回一个条目列表条目列表[expand_key]被删除,并替换为基于expand_key
  • merge_entries(entries, merge_key),返回基于条目的条目列表,其中具有相同条目[merge_key]值的所有条目被组合

def filter_entries(entries, filter_key):
    unique_values = set( [e[expand_key] for e in entries])
    filtered_entries = {}
    for filter_value in unique_values:
        filtered_entries [filter_value] = [e for e in in entries if e[filter_key] == filter_value]
    return filtered_entries 
def expand_entries(entries, expand_key):
    unique_values = set( [e[expand_key] for e in entries])
    new_entries = []
    for entry in entries:
        new_entries.append({key:value for key,value in entries.items() if not key == expand_key})
        for new_key in unique_values:
            new_entries[-1][new_key] = 1 if entry[expand_key] == new_key else 0
    return new_entries

要合并条目,我会断言字符串键值必须是相同的字符串。否则就是一个错误。即合并适当的值看起来像:

2014/01/01       A           100          
2014/01/01       A           60
---------------------------------     
2014/01/01       A           160   

一个糟糕的案例看起来像:

2014/01/01       A           100          
2014/01/01       B           60
---------------------------------     
Value error A , B 

def merge_entries(entries, merge_key):
    unique_keys = set( [e[merge_key] for e in entries])
    new_entries = []
    for key in unique_keys:
        new_entry = None
        for entry in [e for e in entries if e[merge_key] == key]:
            # copy the style of the first entry with that key
            if new_entry is None:
                new_entry = {key:value for key,value in entry.items()}
                for key,value in new_entry.items():
            if not type(value) == str:
                new_entry[key] = 0.0
            for key,value in entry.items():
                if type(value) == str:
                    if not new_entry[key] == value:
                        raise Exception("Cannot merge different string for Key {}: {}, {}".format(key,value, new_entry[key] ))
                else:
                    new_entry[key] += value
        new_entries.append(new_entry)
    return new_entries

最后,通过一点列表理解,顶级代码很简单:

>>> entries = [ {"Date":"aaaa", "ItemType":"A", "Sales":10, "Partner":"P1"},
                {"Date":"aaaa", "ItemType":"A", "Sales":15, "Partner":"P2"},
                {"Date":"cccc", "ItemType":"A", "Sales":15, "Partner":"P2"},
                {"Date":"bbbb", "ItemType":"A", "Sales":15, "Partner":"P2"},
                {"Date":"bbbb", "ItemType":"B", "Sales":10, "Partner":"P3"},
                {"Date":"bbbb", "ItemType":"B", "Sales":15, "Partner":"P2"},
                {"Date":"cccc", "ItemType":"B", "Sales":10, "Partner":"P3"}]
>>> f_entries = filter_entries(entries, "ItemType")
>>> e_entries = {key:expand_entries(entries, "Partner") for key, entries in f_entries.items()}
>>> m_entries = {key:merge_entries(entries, "Date") for key, entries in e_entries.items()}
>>> for key in m_entries.keys():
        print key
        for entry in m_entries[key]:
            print entry  
A
{'Date': 'aaaa', 'P2': 1.0, 'P1': 1.0, 'ItemType': 'A', 'Sales': 25.0}
{'Date': 'cccc', 'P2': 1.0, 'P1': 0.0, 'ItemType': 'A', 'Sales': 15.0}
{'Date': 'bbbb', 'P2': 1.0, 'P1': 0.0, 'ItemType': 'A', 'Sales': 15.0}
B
{'Date': 'cccc', 'P2': 0.0, 'Sales': 10.0, 'ItemType': 'B', 'P3': 1.0}
{'Date': 'bbbb', 'P2': 1.0, 'Sales': 25.0, 'ItemType': 'B', 'P3': 1.0}

我相信有了这个表格,在文件中写表格不会很难!

我不能否认,习惯pandas需要一点时间;我可以说的是,与从头开始实现相比,在IPython控制台上玩并找到有效的东西所需的时间要少得多。

您要查找的基本上是ItemType上的groupby,Partner上的pivot,然后是rolling_sum。有一些巧妙的方法可以非常简洁地做到这一点,但我经常发现,如果我只是将数据分组,根据需要进行处理,然后在最后重新组合组,我会更容易理解我在做什么。

类似的东西

import pandas as pd
df = pd.read_csv("sales.txt", delim_whitespace=True, parse_dates=[0])
df["Sales"] = df["Sales"].str.replace("$","").astype(float)
last_n_dates = 2
processed = []
grouped = df.groupby("ItemType")
for item, group in grouped:
    recent_sales = pd.rolling_sum(group["Sales"], last_n_dates, min_periods=1)
    partners = pd.crosstab(group.Date, group.Partner)
    recent_partners = pd.rolling_sum(partners, last_n_dates, min_periods=1)
    group["Sales"] = recent_sales
    del group["Partner"]
    group = group.set_index("Date")
    new_group = pd.concat([group, recent_partners], axis=1)
    processed.append(new_group)
df_final = pd.concat(processed).fillna(0)

给我

>>> print(df_final)
           ItemType  P1  P2  P3  Sales
Date                                  
2014-01-01        A   0   1   0    100
2014-01-04        A   0   2   0    170
2014-01-08        A   0   1   1    110
2014-01-01        B   1   0   0     60
2014-01-06        B   1   1   0    140
2014-01-09        B   0   2   0    100
[6 rows x 5 columns]

注意,我故意将last_n_dates设置为2,而不是10,因为这里没有足够的值让10变得有趣。但是,110=70+40,所以看起来还可以。

最新更新