Я пытаюсь создать случайные пары из столбца, используя 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) только путем создания «случайных пар этих идентификаторов белков». Откуда берутся эти новые идентификаторы? Это какое-то изменение в коде доступа к белкам при их объединении?