将成对列表转换为尽可能小的 DataFrame 表示形式?
Converting a list of pairs into smallest possible DataFrame representation?
我有一个配对项列表,我想将它们转换成 pandas DataFrame,其中每个配对项在同一列中共享相同的数字。所以像这样:
[('A', 'B'),
('A', 'C'),
('B', 'D')]
转换为...
0 1
A 2 1
B 3 1
C 2 0
D 3 0
因此列按编码对数递减排列,并且使用尽可能少的列。
是否有算法,最好是 numpy 或 pandas 中的算法,可以做到这一点?到目前为止,我一直找不到关于 Google 的任何内容,但自从我学习线性代数以来已经有一段时间了,所以我可能只是忘记了正确使用的术语。
我创建了以下(有问题的)代码来创建 DataFrame,但出于某种原因,它创建了与对一样多的列,这不是我想要完成的。
def create_df(ps):
df = pd.DataFrame(index=np.unique(ps))
cnt = 1
for p in ps:
col = 0
a, b = p
while col in df.columns and (df.at[a, col] != 0 or df.at[b, col] != 0):
col += 1
df.loc[a, col] = cnt
df.loc[b, col] = cnt
cnt += 1
return df
这样做的最终目标是将输出集成到数据管道中,这样我就可以在 pandas 中使用 groupby 来计算对的统计数据。因此,每对必须在同一列中定义,如示例中所示。
这更像是一个 pivot
问题,我们做了 melt
s=pd.DataFrame(l).reset_index().melt('index')
s=s.assign(Col=s.groupby('value').cumcount()).pivot('value','Col','index').\
add(1).fillna(0)
s
Out[62]:
Col 0 1
value
A 1.0 2.0
B 3.0 1.0
C 2.0 0.0
D 3.0 0.0
这是一种使用图形的稀疏矩阵表示的 numpy/scipy 方法。
import numpy as np
from numpy.lib.stride_tricks import as_strided
from scipy import sparse
import pandas as pd
def rerepr_grph(g):
vtx,edg = np.unique(g,return_inverse=True)
npr,nvx = edg.size//2,vtx.size
aux = sparse.csr_matrix(
(np.ones(2*npr),edg,2*np.arange(npr+1)),(npr,nvx)).tocsc()
deg = np.diff(aux.indptr)
srt = (-deg).argsort(kind="stable")
mxdg = deg[srt[0]]
rlr = np.concatenate([aux.indices+1,np.zeros(mxdg,np.int32)])
rlr = as_strided(rlr,(2*npr,mxdg),2*rlr.strides)
szgrps = np.diff(deg.searchsorted(
np.arange(mxdg+1),"right",sorter=srt[::-1]),axis=0)[::-1]
triud = np.array([True,False]).repeat((mxdg,mxdg-1))
triud = as_strided(triud,(mxdg,mxdg),2*triud.strides)
msk = triud.repeat(szgrps,axis=0)
res = np.where(msk,rlr[aux.indptr[srt]],0)
return pd.DataFrame(res,index=vtx[srt])
def rerepr_pd(l):
s=pd.DataFrame(l).reset_index().melt('index')
s=s.assign(Col=s.groupby('value').cumcount()).pivot('value','Col','index').\
add(1).fillna(0)
return s
data = [('A', 'B'),
('A', 'C'),
('B', 'D')]
print(rerepr_grph(data))
print(rerepr_pd(data))
示例 运行(打印 numpy 和 pandas(@WeNYoBen 的代码)对 OP 示例的回答:
0 1
A 1 2
B 1 3
C 2 0
D 3 0
Col 0 1
value
A 1.0 2.0
B 3.0 1.0
C 2.0 0.0
D 3.0 0.0
大型 100 万对示例的速度比较:
# larger example
print()
print("creating larger (1,000,000 pairs) example",end=" ... ")
import itertools as it
from timeit import timeit
A,Z = np.uint32(ord("A")),np.uint32(ord("0"))
data = (np.stack(np.unravel_index(np.random.choice(2600**2,1_000_000,replace=False),(26,10,10,26,10,10)),axis=1).astype("u4") + (A,Z,Z,A,Z,Z)).view('U3')
print("done")
print("benchmarking")
print("numpy ",timeit(lambda:rerepr_grph(data),number=10)*100,"ms")
print("pandas",timeit(lambda:rerepr_pd(data),number=10)*100,"ms")
样本运行:
creating larger (1,000,000 pairs) example ... done
benchmarking
numpy 560.810615005903 ms
pandas 1843.7980080023408 ms
>>> df = pd.DataFrame([('A', 'B'), ('A', 'C'), ('B', 'D')])
首先找到数据框中的所有唯一值:
>>> uniqs = list(pd.unique(df.values.ravel()))
>>> uniqs
['A', 'B', 'C', 'D']
接下来获取出现这些唯一值的索引列表(按相反顺序),将它们添加到字典并从该字典构建数据框:
dict = {}
for uniq in uniqs:
dict[uniq] = list(reversed(df[df.eq(uniq).any(1)].index + 1))
dff = pd.DataFrame({key: pd.Series(value) for key, value in dict.items()}).T
结果:
>>> dff
0 1
A 2.0 1.0
B 3.0 1.0
C 2.0 NaN
D 3.0 NaN
这是一个疯狂的解决方案,仅供娱乐:
s = df.stack().reset_index(name='val')
s = (s.assign(level_1=s.duplicated(['level_1','val'])
.groupby(s['level_0'])
.transform('max')
.add(s['level_1']) % 2,
level_0 = s.level_0 + 1
)
.pivot_table(index='val',
columns='level_1',
values='level_0',
fill_value=0)
)
s.iloc[:,::-1] = np.sort(s, axis=1)
输出:
level_1 0 1
val
A 2 1
B 3 1
C 2 0
D 3 0
我最终在场外找到了适合我的代码,这里是:
def encode_pairs_info(pair_list):
'''
Encode the pair information in multiple columns group_i.
Parameters
----------
pair_list: list
- Example:
pair_list: [('A', 'B'), ('B', 'C')]
Returns
-------
df: pd.DataFrame
- Example:
group_1 group_2
A 1 0
B 1 2
C 0 2
'''
temp = pd.DataFrame(columns=['pair', 'pair_num', 'num', 'group'])
for ipair in range(len(pair_list)):
num_valid_1 = temp[temp.pair == pair_list[ipair][0]]
num_valid_2 = temp[temp.pair == pair_list[ipair][1]]
if num_valid_1.shape[0] == 0:
num_temp_1 = [0]
else:
num_temp_1 = num_valid_1.num
if num_valid_2.shape[0] == 0:
num_temp_2 = [0]
else:
num_temp_2 = num_valid_2.num
num_temp = min(set(range(1, ipair + 2, 1)) - set(num_temp_1) - set(num_temp_2))
temp = temp.append({'ticker': pair_list[ipair][0],
'pair': ipair + 1,
'num': num_temp,
'group': 'group_' + str(num_temp)},
ignore_index = True)
temp = temp.append({'pair': pair_list[ipair][1],
'pair_num': ipair + 1,
'num': num_temp,
'group': 'group_' + str(num_temp)},
ignore_index = True)
df = pd.DataFrame(0,
index=temp.ticker.unique(),
columns=temp.group.unique())
for irow in range(temp.shape[0]):
df.loc[temp.ticker[irow]][temp.group[irow]] = temp.pair_num[irow]
return df
输入:[('A', 'B'), ('A', 'C'), ('B', 'C')]
结果:
group_1 group_2 group_3
A 1 2 0
B 1 0 3
C 0 2 3
我有一个配对项列表,我想将它们转换成 pandas DataFrame,其中每个配对项在同一列中共享相同的数字。所以像这样:
[('A', 'B'),
('A', 'C'),
('B', 'D')]
转换为...
0 1
A 2 1
B 3 1
C 2 0
D 3 0
因此列按编码对数递减排列,并且使用尽可能少的列。
是否有算法,最好是 numpy 或 pandas 中的算法,可以做到这一点?到目前为止,我一直找不到关于 Google 的任何内容,但自从我学习线性代数以来已经有一段时间了,所以我可能只是忘记了正确使用的术语。
我创建了以下(有问题的)代码来创建 DataFrame,但出于某种原因,它创建了与对一样多的列,这不是我想要完成的。
def create_df(ps):
df = pd.DataFrame(index=np.unique(ps))
cnt = 1
for p in ps:
col = 0
a, b = p
while col in df.columns and (df.at[a, col] != 0 or df.at[b, col] != 0):
col += 1
df.loc[a, col] = cnt
df.loc[b, col] = cnt
cnt += 1
return df
这样做的最终目标是将输出集成到数据管道中,这样我就可以在 pandas 中使用 groupby 来计算对的统计数据。因此,每对必须在同一列中定义,如示例中所示。
这更像是一个 pivot
问题,我们做了 melt
s=pd.DataFrame(l).reset_index().melt('index')
s=s.assign(Col=s.groupby('value').cumcount()).pivot('value','Col','index').\
add(1).fillna(0)
s
Out[62]:
Col 0 1
value
A 1.0 2.0
B 3.0 1.0
C 2.0 0.0
D 3.0 0.0
这是一种使用图形的稀疏矩阵表示的 numpy/scipy 方法。
import numpy as np
from numpy.lib.stride_tricks import as_strided
from scipy import sparse
import pandas as pd
def rerepr_grph(g):
vtx,edg = np.unique(g,return_inverse=True)
npr,nvx = edg.size//2,vtx.size
aux = sparse.csr_matrix(
(np.ones(2*npr),edg,2*np.arange(npr+1)),(npr,nvx)).tocsc()
deg = np.diff(aux.indptr)
srt = (-deg).argsort(kind="stable")
mxdg = deg[srt[0]]
rlr = np.concatenate([aux.indices+1,np.zeros(mxdg,np.int32)])
rlr = as_strided(rlr,(2*npr,mxdg),2*rlr.strides)
szgrps = np.diff(deg.searchsorted(
np.arange(mxdg+1),"right",sorter=srt[::-1]),axis=0)[::-1]
triud = np.array([True,False]).repeat((mxdg,mxdg-1))
triud = as_strided(triud,(mxdg,mxdg),2*triud.strides)
msk = triud.repeat(szgrps,axis=0)
res = np.where(msk,rlr[aux.indptr[srt]],0)
return pd.DataFrame(res,index=vtx[srt])
def rerepr_pd(l):
s=pd.DataFrame(l).reset_index().melt('index')
s=s.assign(Col=s.groupby('value').cumcount()).pivot('value','Col','index').\
add(1).fillna(0)
return s
data = [('A', 'B'),
('A', 'C'),
('B', 'D')]
print(rerepr_grph(data))
print(rerepr_pd(data))
示例 运行(打印 numpy 和 pandas(@WeNYoBen 的代码)对 OP 示例的回答:
0 1
A 1 2
B 1 3
C 2 0
D 3 0
Col 0 1
value
A 1.0 2.0
B 3.0 1.0
C 2.0 0.0
D 3.0 0.0
大型 100 万对示例的速度比较:
# larger example
print()
print("creating larger (1,000,000 pairs) example",end=" ... ")
import itertools as it
from timeit import timeit
A,Z = np.uint32(ord("A")),np.uint32(ord("0"))
data = (np.stack(np.unravel_index(np.random.choice(2600**2,1_000_000,replace=False),(26,10,10,26,10,10)),axis=1).astype("u4") + (A,Z,Z,A,Z,Z)).view('U3')
print("done")
print("benchmarking")
print("numpy ",timeit(lambda:rerepr_grph(data),number=10)*100,"ms")
print("pandas",timeit(lambda:rerepr_pd(data),number=10)*100,"ms")
样本运行:
creating larger (1,000,000 pairs) example ... done
benchmarking
numpy 560.810615005903 ms
pandas 1843.7980080023408 ms
>>> df = pd.DataFrame([('A', 'B'), ('A', 'C'), ('B', 'D')])
首先找到数据框中的所有唯一值:
>>> uniqs = list(pd.unique(df.values.ravel()))
>>> uniqs
['A', 'B', 'C', 'D']
接下来获取出现这些唯一值的索引列表(按相反顺序),将它们添加到字典并从该字典构建数据框:
dict = {}
for uniq in uniqs:
dict[uniq] = list(reversed(df[df.eq(uniq).any(1)].index + 1))
dff = pd.DataFrame({key: pd.Series(value) for key, value in dict.items()}).T
结果:
>>> dff
0 1
A 2.0 1.0
B 3.0 1.0
C 2.0 NaN
D 3.0 NaN
这是一个疯狂的解决方案,仅供娱乐:
s = df.stack().reset_index(name='val')
s = (s.assign(level_1=s.duplicated(['level_1','val'])
.groupby(s['level_0'])
.transform('max')
.add(s['level_1']) % 2,
level_0 = s.level_0 + 1
)
.pivot_table(index='val',
columns='level_1',
values='level_0',
fill_value=0)
)
s.iloc[:,::-1] = np.sort(s, axis=1)
输出:
level_1 0 1
val
A 2 1
B 3 1
C 2 0
D 3 0
我最终在场外找到了适合我的代码,这里是:
def encode_pairs_info(pair_list):
'''
Encode the pair information in multiple columns group_i.
Parameters
----------
pair_list: list
- Example:
pair_list: [('A', 'B'), ('B', 'C')]
Returns
-------
df: pd.DataFrame
- Example:
group_1 group_2
A 1 0
B 1 2
C 0 2
'''
temp = pd.DataFrame(columns=['pair', 'pair_num', 'num', 'group'])
for ipair in range(len(pair_list)):
num_valid_1 = temp[temp.pair == pair_list[ipair][0]]
num_valid_2 = temp[temp.pair == pair_list[ipair][1]]
if num_valid_1.shape[0] == 0:
num_temp_1 = [0]
else:
num_temp_1 = num_valid_1.num
if num_valid_2.shape[0] == 0:
num_temp_2 = [0]
else:
num_temp_2 = num_valid_2.num
num_temp = min(set(range(1, ipair + 2, 1)) - set(num_temp_1) - set(num_temp_2))
temp = temp.append({'ticker': pair_list[ipair][0],
'pair': ipair + 1,
'num': num_temp,
'group': 'group_' + str(num_temp)},
ignore_index = True)
temp = temp.append({'pair': pair_list[ipair][1],
'pair_num': ipair + 1,
'num': num_temp,
'group': 'group_' + str(num_temp)},
ignore_index = True)
df = pd.DataFrame(0,
index=temp.ticker.unique(),
columns=temp.group.unique())
for irow in range(temp.shape[0]):
df.loc[temp.ticker[irow]][temp.group[irow]] = temp.pair_num[irow]
return df
输入:[('A', 'B'), ('A', 'C'), ('B', 'C')]
结果:
group_1 group_2 group_3
A 1 2 0
B 1 0 3
C 0 2 3