从 SQLite 数据库中检索和绘制有序的二维热图数据

Retrieving and plotting ordered, 2D heatmap data from a SQLite database

本周大部分时间我都在努力解决这个问题,我想我可能最适合在这里寻求帮助。我将二维数组数据存储在 SQLite 数据库中,我想获取和可视化这些数据。尽管我不确定如何处理,但我需要如何获取和绘制数据有很多限制。

所以,我有一个 table 包含以下内容的数据库:

| ID | SourceID | TargetID | Parameter | Values  |
| 1  |    21    |    34    | 23.46513  | 0.12654 |
| 2  |    21    |    34    | 23.46513  | 0.25478 |
| 3  |    21    |    46    | 23.46513  | 0.43564 |
| 4  |    21    |    46    | 23.46513  | 1.02487 |
| 5  |    34    |    21    | 14.56319  | 0.01476 |
| 6  |    34    |    21    | 14.56319  | 0.87265 |
| 7  |    34    |    46    | 14.56319  | 0.46478 |
| 8  |    34    |    46    | 14.56319  | 0.13665 |
| 9  |    46    |    21    | 7.99581   | 0.04189 |
| 10 |    46    |    21    | 7.99581   | 0.91754 |
| 11 |    46    |    34    | 7.99581   | 0.73688 |
| 12 |    46    |    34    | 7.99581   | 0.24299 |

该数据集的一些特征需要注意:

以下 table 显示每个 SourceId/TargetIdParameter 值的关联:

| SourceID/TargetID | Parameter |
|       46          | 7.99581   |
|       34          | 14.56319  |
|       21          | 23.46513  |

根据我提出的另一个 ,我知道我可以根据最小值将此 table 减少为 (SourceId, TargetId) 的唯一组合Values 使用 SQL 查询,例如:

SELECT SourceID, TargetID, min(Values)
  FROM dataset
  GROUP BY SourceID, TargetID;

这会产生以下减少的 table:

| ID | SourceID | TargetID | SourceSort | Values  |
| 1  |    21    |    34    |  23.46513  | 0.12654 |
| 3  |    21    |    46    |  23.46513  | 0.43564 |
| 5  |    34    |    21    |  14.56319  | 0.01476 |
| 8  |    34    |    46    |  14.56319  | 0.13665 |
| 9  |    46    |    21    |  7.99581   | 0.04189 |
| 12 |    46    |    34    |  7.99581   | 0.24299 |

我遇到的问题是我现在需要根据 Parameter 中的值来订购 table。如果我将 ORDER BY Parameter 添加到 SQL 查询,它会成功地对 SourceID 列进行排序,但是 TargetID 列不会根据此对具有相同 SourceID。简而言之,我想获得以下 table:

| ID | SourceID | TargetID | Values  |
| 12 |    46    |    34    | 0.24299 |
| 9  |    46    |    21    | 0.04189 |    
| 8  |    34    |    46    | 0.13665 |        
| 5  |    34    |    21    | 0.01476 |
| 3  |    21    |    46    | 0.43564 |
| 1  |    21    |    34    | 0.12654 |

此排序的基础示例:首先使用 Parameter 值根据 SourceID 顺序对 table 进行排序。随后,TargetID 基于 Parameter 顺序排序,对于 SourceID 值相等的行,例如,第 1 行 (ID=8) 在第 2 行 (ID=5) 因为 TargetID = 46 应该根据关联的 Parameter 值排在 TargetID = 21 之前。

最后,有了这个table,我需要构建一个热图:

(TargetID ordered by `Parameter ASC`)
 ^
 |
 | 
----------------------------------
21 | 0.04189 | 0.01476 |    0    |
----------------------------------
34 | 0.24299 |    0    | 0.12654 |
----------------------------------
46 |    0    | 0.13665 | 0.43564 |
----------------------------------
   |    46   |    34   |    21  --> (SourceID ordered by `Parameter ASC`)

为此,我使用 Python Pandas 库,并使用 read_sql() 函数执行 SQL 查询。我发现我可以使用 matplotlibpcolor() 函数来绘制热图。我想使用颜色图绘制二维数组中的值,并且我希望数据集的排序与上面完全相同。最后,我想将 SourceIDTargetID 值绘制为刻度标签。

这看起来应该是可行的,但到目前为止,我只能在没有根据 Parameter 排序的情况下生成热图。我不知道我是否最适合直接在 SQL 语句中解决排序位,或者我是否应该只获取数据然后对 [=49 返回的 DataFrame 进行排序=].

无论如何,我将不胜感激!如果有任何不清楚的地方,请告诉我,我会尽力澄清。

谢谢!

您的 sqlite table 格式不正确,因为参数值仅反映 参数与 SourceID 的关联,而不是 TargetID。

最好有两个table:

id_param:

id  parameter
21   23.46513
34   14.56319
46    7.99581

dataset -- 注意这里没有参数栏:

ID  SourceID  TargetID   Values
 1        21        34  0.12654
 2        21        34  0.25478
 3        21        46  0.43564
 4        21        46  1.02487
 5        34        21  0.01476
 6        34        21  0.87265
 7        34        46  0.46478
 8        34        46  0.13665
 9        46        21  0.04189
10        46        21  0.91754
11        46        34  0.73688
12        46        34  0.24299

然后您可以使用 JOIN 在两者之间形成所需的关联 SourceID/Parameter 和 TargetID/Parameter:

    SELECT d.SourceID, d.TargetID, min(d.`Values`) as min_value
      FROM dataset d
      JOIN id_param as ip1
      ON d.SourceID = ip1.id
      JOIN id_param as ip2
      ON d.TargetID = ip2.id
      GROUP BY SourceID, TargetID
      ORDER BY ip1.parameter ASC, ip2.parameter ASC

例如,

import io
import sqlite3
import pandas as pd
import matplotlib.pyplot as plt

text = '''\
| ID | SourceID | TargetID | Parameter | Values  |
| 1  |    21    |    34    | 23.46513  | 0.12654 |
| 2  |    21    |    34    | 23.46513  | 0.25478 |
| 3  |    21    |    46    | 23.46513  | 0.43564 |
| 4  |    21    |    46    | 23.46513  | 1.02487 |
| 5  |    34    |    21    | 14.56319  | 0.01476 |
| 6  |    34    |    21    | 14.56319  | 0.87265 |
| 7  |    34    |    46    | 14.56319  | 0.46478 |
| 8  |    34    |    46    | 14.56319  | 0.13665 |
| 9  |    46    |    21    | 7.99581   | 0.04189 |
| 10 |    46    |    21    | 7.99581   | 0.91754 |
| 11 |    46    |    34    | 7.99581   | 0.73688 |
| 12 |    46    |    34    | 7.99581   | 0.24299 |'''

def make_table(filename):
    # make sqlite table
    with sqlite3.connect(filename) as con:
        df = pd.read_table(io.BytesIO(text), sep=r'\s*[|]\s*').iloc[:, 1:-1]
        df.to_sql('dataset', con=con, if_exists='replace')


filename = '/tmp/data.sqlite'
make_table(filename)

with sqlite3.connect(filename) as con:
    con.execute('DROP TABLE id_param')

    sql = '''CREATE TABLE id_param 
             (id INTEGER PRIMARY KEY, parameter INTEGER)'''
    con.execute(sql)

    sql = '''INSERT INTO id_param
             SELECT SourceID, Parameter
             FROM dataset
             GROUP BY SourceID'''
    con.execute(sql)

    sql = '''
        SELECT d.SourceID, d.TargetID, min(d.`Values`) as min_value
          FROM dataset d
          JOIN id_param as ip1
          ON d.SourceID = ip1.id
          JOIN id_param as ip2
          ON d.TargetID = ip2.id
          GROUP BY SourceID, TargetID
          ORDER BY ip1.parameter ASC, ip2.parameter ASC
    '''
    df = pd.read_sql(sql, con)
    print(df)

产量

   SourceID  TargetID  min_value
0        46        34    0.24299
1        46        21    0.04189
2        34        46    0.13665
3        34        21    0.01476
4        21        46    0.43564
5        21        34    0.12654

将 sqlite table 转换为 Matplotlib pcolor 绘制热图所需的格式的最简单方法(我知道)是使用 Pandas DataFrame 的 pivot 方法。由于此 pivot 将对列和行重新排序,因此无需为 SQL 查询生成的顺序大惊小怪。相反,更容易修复 Pandas:

中的顺序
import io
import sqlite3
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

text = '''\
| ID | SourceID | TargetID | Parameter | Values  |
| 1  |    21    |    34    | 23.46513  | 0.12654 |
| 2  |    21    |    34    | 23.46513  | 0.25478 |
| 3  |    21    |    46    | 23.46513  | 0.43564 |
| 4  |    21    |    46    | 23.46513  | 1.02487 |
| 5  |    34    |    21    | 14.56319  | 0.01476 |
| 6  |    34    |    21    | 14.56319  | 0.87265 |
| 7  |    34    |    46    | 14.56319  | 0.46478 |
| 8  |    34    |    46    | 14.56319  | 0.13665 |
| 9  |    46    |    21    | 7.99581   | 0.04189 |
| 10 |    46    |    21    | 7.99581   | 0.91754 |
| 11 |    46    |    34    | 7.99581   | 0.73688 |
| 12 |    46    |    34    | 7.99581   | 0.24299 |'''

def make_table(filename):
    # make sqlite table
    with sqlite3.connect(filename) as con:
        df = pd.read_table(io.BytesIO(text), sep=r'\s*[|]\s*').iloc[:, 1:-1]
        df.to_sql('dataset', con=con, if_exists='replace')

filename = '/tmp/data.sqlite'
make_table(filename)

with sqlite3.connect(filename) as con:
    sql = '''
        SELECT SourceID, TargetID, min(`Values`) as min_value
          FROM dataset 
          GROUP BY SourceID, TargetID
    '''
    df = pd.read_sql(sql, con)
    table = df.pivot(index='SourceID', columns='TargetID', values='min_value')

    sql = 'SELECT DISTINCT SourceID FROM dataset ORDER BY Parameter ASC'
    order = pd.read_sql(sql, con)['SourceID']
    table = table.reindex(index=order, columns=order)

    fig, ax = plt.subplots()
    ax.pcolor(table.values, cmap=plt.get_cmap('jet'), 
              vmin=df['min_value'].min(), vmax=df['min_value'].max())
    ax.set_xticks(np.arange(table.shape[1] + 1)+0.5, minor=False)
    ax.set_xticklabels(table.columns, minor=False)
    ax.set_yticks(np.arange(table.shape[0] + 1)+0.5, minor=False)
    ax.set_yticklabels(table.index, minor=False)
    ax.set_xlim(0, table.shape[1])
    ax.set_ylim(0, table.shape[0])
    plt.show()

产量