我不知道如何描述我的问题,所以我将在这里展示一个例子。
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亿条记录。所以我正在为这个问题寻找一些高效而干净的代码。
答案 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)
我还建议不使用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
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())