Python:尝试使用Pandas DataFrame创建矩阵

3

我正在使用Pandas将一些来自数据库的数据导入到DataFrame对象中。 数据的格式如下:

time   info    from    to    frequency
19:00  ...      A      X        20
19:00  ...      B      Z         9
21:00  ...      A      Y         2
21:00  ...      A      Z         5
23:55  ...      A      X         8

现在我有两个问题需要解决:

  1. 独立计算从一点到另一点所有移动频率的总和,与时间无关,例如从A点到X点,此总和为28。因此,"时间"和"键"都是可放弃的,在此情况下不需要它们。

  2. 由于我可以保证"from"中的所有点都与"to"中的点相同,因此我希望以某种形式将上述所提到的总和呈现为矩阵。

以下是我已经编写的代码:

import pandas as pd

def make_matrix(df: pd.DataFrame):

    # Get grouped version, discarding date and info...
    grouped = df.groupby(['from', 'to'])['frequency'].sum()

    # Fill dictionary acting as matrix...
    D = {}
    for (_from, _to), freq in grouped.items():
        if D.get(_from):
            D[_from][_to] = int(freq)
        else:
            D[_from] = {}

为了提供背景信息,第一行将示例 DataFrame 转换为:

from    to    frequency
   A     X        28
         Y         2
         Z         5
   B     Z         9

事实上,我相信有更好的方法来做这件事情,但是我在StackOverflow或Google中找不到,因为这是一个非常特殊的情况。

另外,我正在寻找更好的方法,因为这个字典最终会没有每个点X到同一点X的None/0值。

我认为应该有一种更容易合并这些列的方法,而不是以grouped: pd.Series的形式结束,因为必须遍历每个元组,如 (A,X),(A,Y),(A,Z)等,并且还必须人为地将None添加到字典中(X,X)的简单情况中,感觉很糟糕...

编辑1:我正在添加所需的矩阵输出...... 应该类似于这样:

    A     B    ...    X    Y    Z
A  null   0    ...   28    2    5
B   0    null  ...    0    0    9
.
.
.
X   0     0    ...    0    0    0
Y   0     0    ...    0    0    0
Z   0     0    ...    0    0    0

此外,如果在矩阵位置M[X][A]中有另一个元组,例如从XA的频率为25,则该位置上会有0而不是25。 编辑2: 我可能索引错误,可能是转置矩阵而不是示例矩阵,无论哪种情况都存在问题,这是一个非对称的平方矩阵。

1
你能发布所需的矩阵输出吗?make_matrix在我的端上实际上并不起作用。 - tdy
除非你提供了预期输出,否则别人很难理解你的想法。 我认为你想要一个from矩阵,其中所有值都是A,B,X,Y,Z,并且to矩阵中所有值都是A,B,X,Y,Z。 对于矩阵的行A到Z,列A到Z,你想要找到交点以显示频率。 类似这样,其中黄色部分为无,其余部分具有频率(如果存在)。 - Joe Ferndz
1
对不起,我以为很清楚,现在意识到并不是这样。刚刚更新了期望的输出结果。就像@JoeFerndz提供的链接中一样,非常感谢:) - Kvothe
@tdy现在应该可以工作了,我添加了一个缺失的def,我的错 :( - Kvothe
3个回答

1
你可以尝试一下:

(df.groupby(['from', 'to'])['frequency'].sum()
   .unstack(fill_value=0)
)

输出:

to     X  Y  Z
from          
A     28  2  5
B      0  0  9

现在,如果您想要所有可用的目标地点,可以使用 reindex:
all_cols = sorted(set(df['from']).union(set(df['to'])) )

(df.groupby(['from', 'to'])['frequency'].sum()
   .unstack(fill_value=0)
   .reindex(all_cols, fill_value=0)
   .reindex(all_cols, fill_value=0, axis=1)
)

输出:

to    A  B   X  Y  Z
from                
A     0  0  28  2  5
B     0  0   0  0  9
X     0  0   0  0  0
Y     0  0   0  0  0
Z     0  0   0  0  0

你的答案与预期输出不符。 - ThePyGuy

1
df.groupby(['from', 'to'])['frequency'].sum()
   .unstack(fill_value=0)

尝试编写此代码,我相信您将获得正确的输出结果。

3
这与@Quang Hoang的答案有何不同? - Joe Ferndz

0

您可以删除所有列,只保留fromtofrequency。然后对其进行pivot_table操作,以获得所需结果。

import pandas as pd
df = pd.DataFrame ({'time'     :['19:00']*4 + ['20:00']*3 + ['21:00']*4,
                    'from'     :['A','B','A','A','B','X','Y','Z','X','A','Z'],
                    'to'       :['X','Z','Y','Z','X','B','A','B','Z','X','Y'],
                    'frequency':[20 ,  9,  2,  5,  8, 10, 20,  6,  8,  3,  7],
                    'othercols':['junk']*11})

print (df)

df = df[['from','to','frequency']]

df1 = pd.pivot_table(df, values=['frequency'],index=['from'],columns=['to'],aggfunc=sum, margins=False)

print (df1)

这将输出以下内容:
原始数据框:
     time from to  frequency othercols
0   19:00    A  X         20      junk
1   19:00    B  Z          9      junk
2   19:00    A  Y          2      junk
3   19:00    A  Z          5      junk
4   20:00    B  X          8      junk
5   20:00    X  B         10      junk
6   20:00    Y  A         20      junk
7   21:00    Z  B          6      junk
8   21:00    X  Z          8      junk
9   21:00    Y  X          3      junk
10  21:00    Z  Y          7      junk

期望的DataFrame结果:

     frequency                      
to           A     B     X    Y    Z
from                                
A          NaN   NaN  23.0  2.0  5.0
B          NaN   NaN   8.0  NaN  9.0
X          NaN  10.0   NaN  NaN  8.0
Y         20.0   NaN   NaN  NaN  NaN
Z          NaN   6.0   NaN  7.0  NaN

如果行和列之间有映射,并且该值存在,则会进行求和并将其放入网格中。我们有从AX的两个值。它们相加起来(20 + 3)并放入网格中。

如果您希望NaN值为0,则可以给出:

df1 = pd.pivot_table(df, values=['frequency'],index=['from'],columns=['to'],aggfunc=sum, margins=False).fillna(0).astype(int)

输出将会是:

     frequency              
to           A   B   X  Y  Z
from                        
A            0   0  23  2  5
B            0   0   8  0  9
X            0  10   0  0  8
Y           20   0   0  0  0
Z            0   6   0  7  0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接