Я пытаюсь создать случайные пары из столбца, используя DuckDB.
У меня есть столбец белков регистрационных номеров, который выглядит следующим образом:
┌──────────────┐
│ protein_upkb │
│ varchar │
├──────────────┤
│ G1XNZ0 │
│ G1XP19 │
│ G1XP66 │
│ G1XP70 │
│ G1XPL1 │
│ G1XPQ7 │
│ G1XQ23 │
│ G1XQ44 │
│ G1XQ89 │
│ G1XQH2 │
├──────────────┤
│ 10 rows │
└──────────────┘
Я пытаюсь создать случайные пары этих идентификаторов белков, чтобы они выглядели следующим образом:
┌────────────┬────────────┐
│ p1 │ p2 │
│ varchar │ varchar │
├────────────┼────────────┤
│ G1XNZ0 │ G1XP19 │
│ G1XP19 │ G1XP66 │
│ G1XP66 │ G1XP70 │
│ G1XP70 │ G1XPL1 │
│ G1XPL1 │ G1XPQ7 │
│ G1XPQ7 │ G1XQ23 │
│ G1XQ23 │ G1XQ44 │
│ G1XQ44 │ G1XQ89 │
│ G1XQ89 │ G1XQH2 │
│ G1XQH2 │ G1XNZ0 │
├────────────┴────────────┤
│ 10 rows 2 columns │
└─────────────────────────┘
NB: Это всего лишь пример, у меня есть тысячи идентификаторов в рассматриваемой таблице.
Я начал с изменения порядка белков, присваивая каждой строке случайное число и сортируя по нему.
CREATE VIEW proteins AS
SELECT protein_upkb, random() as x FROM read_parquet('mini_proteins.parquet');
SELECT * FROM proteins ORDER BY x DESC LIMIT 10;
Что приводит к
┌──────────────┬────────────────────┐
│ protein_upkb │ x │
│ varchar │ double │
├──────────────┼────────────────────┤
│ A0A1H6HM63 │ 0.9999986232724041 │
│ A0A1G6CK58 │ 0.9999978158157319 │
│ A0A2C5XBA3 │ 0.9999923389405012 │
│ A0A1H9T955 │ 0.9999855090864003 │
│ Q05Q16 │ 0.9999655580613762 │
│ R5PE70 │ 0.999956940067932 │
│ R5GUN0 │ 0.9999453630298376 │
│ A0A0L0UJ42 │ 0.9999357375781983 │
│ W6ZJY1 │ 0.9999311361461878 │
│ F6D0F2 │ 0.9999301459174603 │
├──────────────┴────────────────────┤
│ 10 rows 2 columns │
└───────────────────────────────────┘
Затем я попытался создать случайные пары, используя подзапросы. Один столбец будет отсортирован по x
по убыванию, другой по x
по возрастанию.
К моему удивлению, это создает только одну случайную пару, а не 255 622. Я и ожидал, и нуждался.
cursor = duckdb.sql("""
SELECT
(SELECT protein_upkb FROM proteins ORDER BY x DESC) as p1,
(SELECT protein_upkb FROM proteins ORDER BY x ASC) as p2,
LIMIT 10;
""").show()
┌─────────┬─────────┐
│ p1 │ p2 │
│ varchar │ varchar │
├─────────┼─────────┤
│ Q28RH7 │ D8LJ06 │
└─────────┴─────────┘
Я решил, что могу создать два представления: proteins1
и proteins2
. Затем я могу самостоятельно случайным образом отсортировать их, используя random()
, как я делал это раньше.
Наконец, я могу создавать пары, выбирая столбец protein_upkb
в каждой таблице.
Еще раз: я немного удивлен результатом.
p2
— это последовательность случайных белков, а p1
— это всего лишь один из белков.
cursor = duckdb.sql("""
CREATE VIEW proteins1 AS
SELECT protein_upkb, random() as x FROM read_parquet('mini_proteins.parquet')
ORDER BY x ASC;
CREATE VIEW proteins2 AS
SELECT protein_upkb, random() as x FROM read_parquet('mini_proteins.parquet')
ORDER BY x ASC;
SELECT ps1.protein_upkb as p1, ps2.protein_upkb as p2,
FROM proteins1 as ps1, proteins2 as ps2
LIMIT 10;
""").show()
┌────────────┬────────────┐
│ p1 │ p2 │
│ varchar │ varchar │
├────────────┼────────────┤
│ A0A394DPL7 │ A0A1I3L166 │
│ A0A394DPL7 │ A0A0Q3WJP1 │
│ A0A394DPL7 │ A0A093SP34 │
│ A0A394DPL7 │ A0A127EQY9 │
│ A0A394DPL7 │ K6UP11 │
│ A0A394DPL7 │ A0A1I6M9F9 │
│ A0A394DPL7 │ A0A0Q3SWF8 │
│ A0A394DPL7 │ A0A069RD68 │
│ A0A394DPL7 │ S9ZHA8 │
│ A0A394DPL7 │ Q5P5L0 │
├────────────┴────────────┤
│ 10 rows 2 columns │
└─────────────────────────┘
Вы можете проверить это в блокноте Colab.
@ThorstenKettner Ах да, это очень неясно из того, как я задал этот вопрос. В этой таблице очень много строк, и я показываю только первые 10. Когда вы шифруете, первые 10 представляют собой новый набор идентификаторов. Я внесу правку, чтобы было понятнее, спасибо.
Я не знаю дакдб. Но это вызовет ошибку времени выполнения почти во всех СУБД:
SELECT
(SELECT protein_upkb FROM proteins ORDER BY x DESC) as p1,
(SELECT protein_upkb FROM proteins ORDER BY x ASC) as p2,
LIMIT 10
Ну, во-первых, после p2 слишком много запятой. Но этот запрос выбирает одну строку, потому что вы выбираете из ничего (в основном предложении SELECT
нет предложения FROM
). Эта одна строка имеет два столбца: p1 и p2. Теперь все в порядке, пока два подзапроса возвращают только одну строку, но из вашей таблицы мы знаем, что это не так. Запрос пытается заполнить каждую из двух ячеек в строке результата всеми значениями таблицы. Обычно это приводит к ошибке. Однако я вижу, что DuckDB поддерживает типы данных массива, поэтому я предполагаю, что эти два столбца представляют собой своего рода массивы. (Тогда даже ORDER BY
может иметь некоторый эффект, чего не происходит в стандартном SQL, где ORDER BY
в подзапросе является излишним, поскольку результаты подзапроса по определению представляют собой неупорядоченные наборы данных.)
Что касается представлений: в SQL все является таблицей :-D Существуют хранимые таблицы, такие как ваша таблица белков. Существуют таблицы результатов как результат всех наших запросов. Существуют результаты подзапроса, которые также являются таблицами. И есть мнения, которые точно такие же. Таблицы представляют собой неупорядоченные наборы данных. Следовательно, ORDER BY
в представлении не имеет смысла, поскольку СУБД может его полностью игнорировать.
В любом случае вы перекрестно объединяете два представления, т. е. объединяете каждую строку первого представления с каждой строкой второго представления. То, что вы видите, является частью результата, а именно первой строкой первого представления в сочетании с первыми десятью строками второго представления. (Так что, возможно, СУБД даже приложила немало усилий, чтобы удовлетворить ваши предложения ORDER BY
:-) Вы видите только первые 10 результатов, конечно, из-за примененного вами предложения LIMIT
.
Вместо этого вы хотите объединить случайные строки с другими случайными строками из той же таблицы. Тогда это зависит от того, сколько случайности вы хотите. Если вы просто хотите получить несколько случайных пар, в которых вы не получаете белка дважды, вы можете сделать:
WITH
data AS
(
SELECT protein_upkb, ROW_NUMBER() OVER (ORDER BY random()) AS sortkey
FROM proteins
ORDER BY random()
)
SELECT
FIRST(protein_upkb ORDER BY sortkey) AS code1,
LAST(protein_upkb ORDER BY sortkey) AS code2
FROM data
GROUP BY ((sortkey - 1) // 2) -- Integer division 0, 0, 1, 1, 2, 2, etc.
LIMIT 10;
Или, если вас устраивают дубликаты, вы можете просто
SELECT
a.protein_upkb AS code1,
b.protein_upkb AS code2
FROM proteins a CROSS JOIN proteins b
ORDER BY random()
LIMIT 10;
который может дать вам такой результат, как
Есть несколько способов сделать это. Точный способ будет зависеть от ваших дополнительных требований.
Это очень просто: дважды возьмите случайную выборку, объедините их вместе. Вы можете получить один и тот же белок дважды, но можете исключить его с помощью дополнительного пункта where
.
duckdb.sql("""
with cte as (
select protein_upkb from proteins using sample(10)
)
select *
from cte as c1
positional join cte as c2
""")
┌──────────────┬──────────────┐
│ protein_upkb │ protein_upkb │
│ varchar │ varchar │
├──────────────┼──────────────┤
│ A0A0F6TCU1 │ A0A4C1ULV9 │
│ D4YJT4 │ A0A3Q3FTK5 │
│ A0A319DTU8 │ C6LIN2 │
│ A0A1Q3D9X9 │ A0A1B3BCY8 │
│ M5F4R3 │ M1NUZ3 │
│ A0A553PJQ2 │ A0A165P0W9 │
│ G7M9F2 │ A0A182JZX3 │
│ A0A0Q1CIG2 │ G3HMK9 │
│ C7YU85 │ A0A3Q2E7T6 │
│ A0A199VI77 │ A0A0R1JQR6 │
├──────────────┴──────────────┤
│ 10 rows 2 columns │
└─────────────────────────────┘
Или назначьте row_number случайным образом, а затем поверните таким образом, чтобы даже нечетные строки создавали пары:
duckdb.sql("""
with cte1 as (
select ps1.protein_upkb, row_number() over(order by random()) as rn
from proteins as ps1
), cte2 as (
select
protein_upkb,
rn % 2 as col,
rn // 2 as r
from cte1
)
pivot cte2
on col
using any_value(protein_upkb)
limit 10
""")
───────┬────────────┬────────────┐
│ r │ 0 │ 1 │
│ int64 │ varchar │ varchar │
├───────┼────────────┼────────────┤
│ 66322 │ A0A1N7AVA0 │ A0A175R4H7 │
│ 66325 │ K9FKM7 │ D8QP02 │
│ 66327 │ A0A1I5KRT3 │ W0V524 │
│ 66328 │ A0A4U2YU79 │ A0A452RP46 │
│ 66334 │ A8RCK1 │ A0A165U1L8 │
│ 66335 │ A0A3Q3QVI9 │ C7MCJ1 │
│ 66336 │ Q3SLR9 │ A0A3B4B0Q2 │
│ 66338 │ A0A1W1XBB2 │ A0A0B7J5C1 │
│ 66339 │ A0A1I4KH70 │ A0A3S4SEU1 │
│ 66340 │ A0A1W0D573 │ Q4ZR49 │
├───────┴────────────┴────────────┤
│ 10 rows 3 columns │
└─────────────────────────────────┘
from cte as c1 positional join cte as c2
действительно выглядит странно :-) Можно было бы ожидать, что каждая строка будет присоединена сама к себе, но в отличие от других СУБД (Oracle, SQL Server, PostgreSQL, MySQL), Duckdb, похоже, выдает новый результат CTE каждый раз, когда на CTE ссылаются. Это кажется странным, и кто знает, собираются ли они изменить это в каком-нибудь будущем выпуске.
Да, вы также можете создать для этого 2 разных ctes :)
Большое спасибо, второй ответ - это именно то, что я ищу :)
Я не понимаю, как можно получить от 10 идентификаторов, начинающихся с G1, до пар типа (A0A147DQS2 │ A0A1X0GRM6) только путем создания «случайных пар этих идентификаторов белков». Откуда берутся эти новые идентификаторы? Это какое-то изменение в коде доступа к белкам при их объединении?