返回按另一列分组的元素列表

时间:2018-03-29 21:11:45

标签: python pandas dataframe group-by

我不知道如何描述我的问题,所以我将在这里展示一个例子。

A B
1 3
1 4
2 5
2 8
2 6
3 6
3 8
4 10
4 1

数据框有两列A和B.我想让它返回一个这样的列表。

[[3,4],[5,8,6],[6,8],[10,1]]

如您所见,这是按A分组,并返回B列中的数字列表。值得注意的是,B中元素的顺序不会改变。此外,子列表的顺序与A列中的顺序相同。(第1组[3,4],第2组[5,8,6]等)

假设数据帧已由A排序。我知道如何使用for循环来完成它,但我的数据集有10亿条记录。所以我正在为这个问题寻找一些高效而干净的代码。

4 个答案:

答案 0 :(得分:2)

首先需要对第一列A进行分组,然后在B中获取唯一值(假设您只需要唯一值而不是重复值)。完成后,使用lambda表达式将每个np.array值转换为列表,然后使用.tolist()将结果序列转换为列表。

>>> df.groupby('A', sort=False)['B'].apply(list).tolist()

或者,

>>> [list(v) for v in df.groupby('A', sort=False)['B'].unique()]

或者,

>>> df.groupby('A', sort=False)['B'].apply(lambda x: x.unique().tolist()).tolist()

[[3, 4], [5, 8, 6], [6, 8], [10, 1]]

我还建议不要对groupby操作进行排序。

以下是对任何感兴趣的人的时间比较:

df_ = pd.concat([df] * 10000)  # Set-up larger dataframe with 90k rows.

%timeit df_.groupby('A', sort=False)['B'].unique().apply(list).tolist()
# 100 loops, best of 3: 5.9 ms per loop

%timeit df_.groupby('A', sort=False)['B'].apply(list).tolist()
# 100 loops, best of 3: 6.79 ms per loop

%timeit list(map(list, df_.groupby('A', sort=False)['B'].apply(list)))
# 100 loops, best of 3: 8.02 ms per loop

答案 1 :(得分:1)

@Alexander's solution的替代方法是将list应用于groupby.apply(list)对象的每个元素。

一般来说,我更倾向于使用基于lambda的解决方案,这只是一个循环。

res = list(map(list, df.groupby('A', sort=False)['B'].apply(list)))

结果:

[[3, 4], [5, 8, 6], [6, 8], [10, 1]]

答案 2 :(得分:1)

对于大型数据集,我建议使用Numpy,因为它更快。

我还建议使用for循环进行排序,df.sort_values()的速度更快。

这里是我研究的1500万行数据集的比较。

通过Numpy

import numpy as np
df = df[['a','b']]

keys, values = df.sort_values('a').values.T
ukeys, index = np.unique(keys, True)
arrays = np.split(values, index[1:])
df = pd.DataFrame({'a':ukeys,'b':[list(a) for a in arrays]})
NUMPY
Total time: 102.379 s for 15,397,309 rows
Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
     3         1    1205208.0 1205208.0      1.2    
     4         1   60671365.0 60671365.0     59.3   
     5         1   16897187.0 16897187.0     16.5    
     6         1    1430774.0 1430774.0      1.4     
     7         1   22174794.0 22174794.0     21.7     
     8         1          4.0      4.0      0.0      

使用熊猫“ Groupby”

df.groupby('a')['b'].apply(list)
PANDAS GROUPBY
Total time: 146.23 s for 15,397,309 rows

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
     3         1    1181714.0 1181714.0      0.8     
     4         1  145048477.0 145048477.0     99.2      
     5         1          3.0      3.0      0.0      

答案 3 :(得分:0)

有不同的方法:

数据是:

public async void Upload_FileAsync(string WebServiceURL, string FilePathToUpload)
{

    //prepare HttpStreamContent
    IStorageFile storageFile = await StorageFile.GetFileFromPathAsync(FilePathToUpload);

    //Here is the code we changed
    IRandomAccessStream stream=await storageFile.OpenAsync(FileAccessMode.Read);
    Windows.Web.Http.HttpStreamContent streamContent = new Windows.Web.Http.HttpStreamContent(stream);

    //send request
    var myFilter = new Windows.Web.Http.Filters.HttpBaseProtocolFilter();
    myFilter.AllowUI = false;
    var client = new Windows.Web.Http.HttpClient(myFilter);
    Windows.Web.Http.HttpResponseMessage result = await client.PostAsync(new Uri(WebServiceURL), streamContent);
    string stringReadResult = await result.Content.ReadAsStringAsync();
}

使用collections.defaultdict()

的第一种方法
with open('textrr','r') as f:
    data=[line.split() for line in f.readlines()]

输出:

订单不一样:

d=collections.defaultdict(list)

for item in data:
    d[item[0]].append(item[1])

print([i for i in d.values() if i[0].isdigit()])

使用itertools.grouby:

[['10', '1'], ['6', '8'], ['3', '4'], ['5', '8', '6']]

输出:

订单相同

import itertools
print([[sub[1] for sub in i] for j,i in itertools.groupby(data,key=lambda x:x[0]) if list(j)[0].isdigit()])

最后,如果您不想使用任何导入,那么您可以尝试手动方法:

[['3', '4'], ['5', '8', '6'], ['6', '8'], ['10', '1']]

输出:

groupby={}

for item in data:
    if item[0].isdigit() and item[0] not in groupby:
        groupby[item[0]]=[item[1]]
    elif item[0].isdigit():
        groupby[item[0]].append(item[1])

print(groupby.values())