【问题标题】:How to apply panda group by with minimum and maximum size condition (Pythonic way)如何在最小和最大尺寸条件下应用熊猫组(Pythonic方式)
【发布时间】:2022-01-20 11:57:09
【问题描述】:

我在 pandas 中有一个数据框,我需要将其分组并存储在一个新数组中,我需要每个组的大小具有特定大小,如果一个超过最小大小,则应将其添加到之前的组之一尺寸最小的。例如,在对数据进行分组后,我将拥有 G 组,即 len(G)<=b、len(G)>=a 或 a <= len(G) <= b。所以,我需要用len(G)>=a 使组满足a <= len(G) <= b 的条件。

代码正在运行。所以,我想知道是否有更方便的方法来做到这一点。

import numpy as np
import pandas as pd

rng = np.random.default_rng()  # Just for testing
df = pd.DataFrame(rng.integers(0, 10, size=(1000, 4)), columns=list('ABCD'))
# The dataframe is grouped depend on specific column.
ans = [pd.DataFrame(y) for x, y in df.groupby(df.columns[3], as_index=False)] 

n = 20 # The maximum size of the group is 25

new_arrayi_index = 0
new_array = []
for count_index in range(len(ans)):
    l = ans[count_index]
   
    if len(l) > n:

        df_shuffled = pd.DataFrame(l).sample(frac=1)
        final = [df_shuffled[i:i+n] for i in range(0,df_shuffled.shape[0],n)]

        for inde in range(len(final)):
            if len(final[inde]) <= 5 and new_arrayi_index != 0: #The minimum size of the group is 5

                new_array[new_arrayi_index - 1]=new_array[new_arrayi_index - 1]+final[inde]

            else:
                new_array.append(final[inde])
                new_arrayi_index += 1

    else: 

        new_array.append(l)
        new_arrayi_index += 1

count_index_ = 0
for count_index in range(len(new_array)):
    print("count", count_index, "Size", len(new_array[count_index]))
    print(new_array[count_index])
    count_index_ += count_index

print(count_index_)

【问题讨论】:

  • 您能否提供一些最小示例数据和其余变量(例如ans),以便我们仅使用问题中的信息运行您的代码?我可以立即发现一些东西(例如len(l)//n),但要正确重构它,我需要能够先运行它。
  • 我已经添加了 cmets 来解释这些变量。
  • 您的标题表明您需要大小为a &lt;= len(l) &lt;= b 的样本,为此您可以删除项目或重复项目。这是正确的吗?
  • 我想要一个大小为a &lt;= len(l) &lt;= b 的样本,但如果该组不能满足条件,我必须将其添加到最小的组中。重复的项目可以在那里。假设我有 3 组 20 个元素,最后一组是一个。所以,我应该把那个加到 20 中。
  • 所以,您想将数据拆分为一个暗淡列表N x num_cols,这里的 N 为 20。理想情况下,您希望new_array 中的所有条目的大小为 20,并且单个条目可能更小(但如果它小于最小值,则应该略大于 20)对吗?各个条目的顺序重要吗?

标签: python pandas group-by


【解决方案1】:

我从一开始就关注这个帖子,很好奇讨论会如何,因为 OP 的问题并不总是可以解决。

存在解决方案

举个例子:一个组有 19 个元素,你想把它分成大小在 10 到 15 之间的部分。

当且仅当存在整数 g 时,解决方案才存在,例如 n/b &lt;= g &lt;= n/a。在这种情况下,您可以看到长度为a 的g 部分将使用g*a &lt;= n 元素,而长度为b 的部分将使用g*b &gt;= n。

在这种情况下,也可以有一个平衡分区,即最大的部分最多比最小的部分大一个记录(最小的部分将有n//g 记录)。

重述问题

我们可以对问题进行轻微修改,将其拆分为尽可能少的部分,每个部分最多包含 b 记录。使得每个部分的长度满足a &lt;= len(s) &lt;= a+1。

请注意,在这种情况下,我们将a 调整为与b 最接近,以便问题得到解决。对于可解决的问题,解决方案将是原始问题的解决方案,对于无法解决的问题,将通过减少a来修改原始需求,从而解决问题。

上面的示例将变为:在不超过 15 个元素的尽可能少的平衡组中拆分 19 个元素。那么解决方案是包含 10 个元素的部分和 9 个元素的部分。

pythonic 解决方案

def group_and_split(df, b, column):
    '''
    - df    : a datafame
    - b     : the largest allowed section
    - column: the column by which the data must be grouped
    '''
    
    # doing it in a pythonic way
    return [[y] if len(y) <= n else np.array_split(y, (len(y)+b-1)//b)
             for x, y in df.groupby(column, as_index=False)]

您可以检查它是否为重述的问题提供了解决方案

pd.DataFrame([{
    'num-sections': len(g), 
    'largest-section': max(len(gi) for gi in g), 
    'smallest-sections':min(len(gi) for gi in g)
} for g in group_and_split(df, 25, 'D')])

完整的运行代码

import pandas as pd
import numpy as np

rng = np.random.default_rng(1)  # Just for testing
df = pd.DataFrame(rng.integers(0, 10, size=(1000, 4)), columns=list('ABCD'))

def group_and_split(df, b, column, n=1):
    '''
    - df    : a datafame
    - b     : the largest allowed section
    - column: the column by which the data must be grouped
    - n     : don't split groups that are smaller than this
    '''

    # doing it in a pythonic way
    return [[y] if len(y) <= n else np.array_split(y, (len(y)+b-1)//b)
             for x, y in df.groupby(column, as_index=False)]



pd.DataFrame([{
    'num-sections': len(g), 
    'largest-section': max(len(gi) for gi in g), 
    'smallest-sections':min(len(gi) for gi in g)
} for g in group_and_split(df, 25, 'D', 93)])

打印所有组

for group in group_and_split(df, 25, 'D'):
    for section in group:
        print(section)

【讨论】:

  • 它不适用于我的数据,它显示了一些较小的组并显示AttributeError: 'str' object has no attribute 'itertuples'ibb.co/s9BQJYB
  • 据我所知,它适用于您提供的数据。我添加了完整的运行代码,方便您检查。熊猫 1.3.2,numpy 1.19.5 在这里。
  • 能否把每组的数据显示一下?
  • 请仔细查看group_and_split 的输出。它将给出一个列表列表,每个列表对应一个组。您拥有可以查看任何您想要的代码的代码。例如group_and_split(df, 25, 'D')[2][0] 将给出第三组的第一部分。
  • 我尝试显示它们,它显示前 5 个组,然后显示 AttributeError: 'str' object has no attribute 'itertuples'
【解决方案2】:

我编写了一个函数,将数据帧拆分为等于最大大小的块。它检查最后一个块的余数大小,如果余数小于最小大小,则将最后两个块分成大小大致相等的两个块。

在Split a large pandas dataframe 建立答案

import numpy as np
import pandas as pd


rng = np.random.default_rng(seed=1)  # Just for testing
df = pd.DataFrame(rng.integers(0, 10, size=(1000, 4)), columns=list('ABCD'))
# The dataframe is grouped depend on specific column.

n = 20  # The maximum size of the group is 25


# https://stackoverflow.com/questions/17315737/split-a-large-pandas-dataframe

def split_dataframe(df, chunk_size=20, min_size=10):

    chunks = list()
    remainder = len(df) % chunk_size

    if 0 < remainder < min_size:
        num_chunks = len(df) // chunk_size - 1
        for i in range(num_chunks):
            chunks.append(df[i * chunk_size:(i + 1) * chunk_size])
        df_ = df[(num_chunks) * chunk_size:]
        last_break = int(len(df_) / 2)
        chunks.append(df_[:last_break])
        chunks.append(df_[last_break:])
        return chunks
    else:
        num_chunks = len(df) // chunk_size + 1
        if remainder == 0:
            num_chunks += -1
        for i in range(num_chunks):
            chunks.append(df[i*chunk_size:(i+1)*chunk_size])
        return chunks


new_array = []
for group, df_ in df.groupby(df.columns[3], as_index=False):
    if len(df_) > n:
        new_array.extend(split_dataframe(df_))
    else:
        new_array.extend(df_)

count_index_ = 0
for count_index in range(len(new_array)):
    print("count", count_index, "Size", len(new_array[count_index]))
    print(new_array[count_index])
    count_index_ += count_index

print(count_index_)

【讨论】:

  • 谢谢,我只需要拆分大小超过20的组。另外,运行代码后,我得到了一个大小为0的组。
  • 我认为问题在于某些数据帧完全可以被 n 整除,所以我添加了几行来检查余数是否 == 0:num_chunks += -1。让我知道是否可以解决它。如果数据帧的行数少于 n,我添加了一个检查以跳过该函数,但这实际上不是必需的。
  • 我已添加到我的代码中,它显示了一些 AttributeError: 'str' object has no attribute 'itertuples' 列表的错误并显示缺少项目。
【解决方案3】:

改变这一行 -> ans = [pd.DataFrame(y) for x, y in df.groupby(df.columns[3], as_index=False)] 至 ans = [pd.DataFrame(y) for x, y in df.groupby(df.columns[3].min(), as_index=False)] 最少

和ans = [pd.DataFrame(y) for x, y in df.groupby(df.columns[3].max(), as_index=False)] 最大

【讨论】:

  • 谢谢,但我不需要按最小值或最大值对其进行分组。我需要将组本身拆分为特定的大小。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-28
  • 2016-10-27
  • 2017-10-12
  • 2022-08-04
  • 1970-01-01
相关资源
最近更新 更多