Python数据聚合("Countifs"函数在Excel)

Python data aggregation ("Countifs" function in Excel)

Excel 老用户在这里变成了 Python 新用户。我有以下产品 ID 数据框:

productID              sales
6976849                194,518,557             
11197085               277,387,647
70689391               197,511,925
70827164               242,995,691
70942756               1,529,319,200

(它在界面中看起来不太漂亮,但在 Python 中,我设法将它放入一个数据框中,其中包含一个 ID 列和一个销售列。)

每个产品ID都有一个销售总数。

我需要统计有多少产品销售额超过200,000,000,有多少产品销售额低于200,000,000。

桶总数 超过 200,000,000 x 200,000,000 岁以下

在 Excel 中,我会使用快速 Countif 函数来执行此操作,但我不确定它在 Python 中的工作原理。

我很难找到如何做到这一点——任何人都可以指出我正确的方向吗?即使只是函数的名称,以便我可以阅读它们,也会很有用!

谢谢!!

使用Pandasvalue_counts:

import pandas as pd

df = pd.read_excel('data.xlsx')
over, under = df['sales'].gt(200000000).value_counts().tolist()

输出:

>>> over
3

>>> under
2

一步一步:

# Display your data after load file
>>> df
   productID       sales
0    6976849   194518557
1   11197085   277387647
2   70689391   197511925
3   70827164   242995691
4   70942756  1529319200

# Select the column 'sales'
>>> df['sales']
0     194518557
1     277387647
2     197511925
3     242995691
4    1529319200
Name: sales, dtype: int64

# Sales are greater than 200000000? (IF part of COUNTIF)
>>> df['sales'].gt(200000000)
0    False
1     True
2    False
3     True
4     True
Name: sales, dtype: bool

# Count True (over) and False (under) (COUNT part of COUNTIF)
>>> df['sales'].gt(200000000).value_counts()
True     3
False    2
Name: sales, dtype: int64

# Convert to list
>>> df['sales'].gt(200000000).value_counts().tolist()
[3, 2]

# Set variables over / under
>>> over, under = df['sales'].gt(200000000).value_counts().tolist()

更新

I should also add that there are 100 million rows in the dataset, and I will need more buckets, something like Over 500 million between 200 million and 500 million between 100 million and 200 million under 100 million Can you tell me how I would go about setting the buckets?

使用pd.cut and value_counts:

df['buckets'] = pd.cut(df['sales'], right=False, ordered=True,
                       bins=[0, 100e6, 200e6, 500e6, np.inf],
                       labels=['under 100M', '100-200M',
                               '200-500M', 'over 500M'])
>>> df
   productID       sales    buckets
0    6976849   194518557   100-200M
1   11197085   277387647   200-500M
2   70689391   197511925   100-200M
3   70827164   242995691   200-500M
4   70942756  1529319200  over 500M

>>> df.value_counts('buckets', sort=False)
buckets
under 100M    0
100-200M      2
200-500M      2
over 500M     1
dtype: int64

有很多非常强大的库可以帮助您快速完成任务,但您提到您才刚刚起步,所以我建议您只使用 python 本身作为一种学习体验.

Excel 可以将文件保存为“CSV”,这是一种非常简单的文本文件格式。 CSV 文件通常只是电子表格中每一行的逐行表示,列之间用逗号分隔。您的问题的一个非常简单的解决方案可能是逐行读取此类文件,确定销售数量是否大于或小于给定数量,然后将一个添加到适当的组中。

开始阅读文件非常简单。用代码读取文件与您可能习惯的有点不同,但它非常简单,并且在不同的编程语言之间几乎总是相同的。首先你“打开”文件,它基本上要求操作系统进行访问。 OS 将确定您是否应该具有访问权限,如果是,将使您能够访问相当于可以扫描文件的游标。这个“光标”通常被称为“文件句柄”,或者在 python 中简称为“文件 object”。

file_obj = open("data.csv", "r")  #"r" for reading mode (instead of write)

一旦我们有了句柄,我们就可以调用它的read方法来读取部分或全部文件。传递一个数字将告知要读取多少个字符(换行符和一些其他不可见代码算作一个字符),或者在 python 中你可以不传递任何参数,它将读取整个文件。因为我们以文本模式(默认)打开文件,所以我们将得到一个常规字符串。

file_contents = file_obj.read()

现在我们已经将文件的内容复制到一个字符串中,我们可以关闭文件并告诉 OS 我们已经完成了它。

file_obj.close()

您可能经常在 python 中看到这一点,使用 with 上下文稍微清理一下代码,并确保文件始终关闭:

with open("data.csv", "r") as file_obj:
    file_contents = file_obj.read()

现在您有一个包含整个 CSV 文件的长字符串。我们希望能够逐行进行,因此我们必须在找到 new-line 字符的任何地方将字符串分成多个部分。 Python 字符串使用 str.split 方法可以方便地做到这一点:

file_lines = file_contents.split("\n") #split the string into a list of strings by splitting on \n chars

现在我们将使用此列表创建一个循环来读取每一行,并确定该产品的销售额是多于还是少于 200,000,000。为此,我们必须找到每一行的适当部分,将其转换为数字,并使用 if 语句来决定如何处理它。 python 循环的最简单形式是遍历简单项目列表中的每个项目:for line in file_lines:。现在在循环内的每次迭代中,line 变量将填充文件该行中的任何内容。只要 csv 文件没有空行,也没有 headers,我们几乎只需要找到第二个值(以逗号分隔),并将其转换为数字。这里我们将再次使用 str.split 方法,在逗号处拆分字符串,并将第二项转换为整数(记住 python 使用基于 0 的索引,因此第二项将是索引 1 ).从这里我们可以添加一些逻辑来确定我们是否应该为 2 亿以下或超过 2 亿算一个。

over = 0
under = 0
for line in file_lines:
    line_items = line.split(",")
    sales = int(line_items[1])
    if sales > 200000000:
        over = over + 1
    else:
        under = under + 1

在阅读了您关于需要更多“桶”的评论后,终于有了一个更高级的版本

buckets = [0] * 8 #list of 8 buckets each starting at 0
bucket_edges = [0, 1e6, 2e6, 5e6, 1e7, 2e7, 5e7, 1e8, 2e8] #an even more advanced version would find the max and min of the sales figures, and dynamically calculate the bucket edges
with open("data.csv") as f: #"r" mode is actually the default
    sales = [] #an empty list
    for line in f: #a default behavior of file handles is to read line-by-line in for loops
        line = line.strip() #remove leading and trailing whitespace
        if not line: #an empty string will act like "False"
            continue #advance to the next loop iteration
        sales.append(int(line.split(",")[1])) #combine a few operations in a single line, and build up a list of sales figures
for sale in sales:
    for i in range(len(buckets)):
        if bucket_edges[i] <= sale < bucket_edges[i+1]:
            buckets[i] += 1
for i in range(len(buckets)):
    print(bucket_edges[i], "to", bucket_edges[i+1], ":", buckets[i])

如果使用 convtools 并将 xlsx 文件转换为 csv,那么简短的回答是:

# pip install convtools
from convtools import conversion as c

converter = c.aggregate({
    "below_200": c.ReduceFuncs.Count(where=c.item("sales") < 200000000),
    "above_200": c.ReduceFuncs.Count(where=c.item("sales") >= 200000000),
}).gen_converter()
results = converter(list_of_dicts)

更长的一个:

import csv

# pip install convtools
from convtools import conversion as c

buckets = [
    (None, 200000000),
    (200000000, 220000000),
    (220000000, 240000000),
    (220000000, None),
]


def bucket_to_condition(bucket, input_):
    conditions = []
    left, right = bucket
    if left is not None:
        conditions.append(input_ >= left)
    if right is not None:
        conditions.append(input_ < right)
    if not conditions:
        return {}
    return {
        "where": c.and_(*conditions) if len(conditions) > 1 else conditions[0]
    }


with open("input_data.csv", "w") as f:
    reader = csv.reader(f)

    # skip header (assuming it's known)
    next(header)

    converter = (
        c.iter(
            {
                "productID": c.item(0),
                "sales": c.item(1).as_type(int),
            }
        )
        .pipe(
            c.aggregate(
                {
                    f"{bucket}": c.ReduceFuncs.Count(
                        **bucket_to_condition(bucket, c.item("sales")),
                    )
                    for bucket in buckets
                }
            )
        )
        .gen_converter()
    )
    results = converter(reader)