Создание случайных пар из столбца

Проблема

Я пытаюсь создать случайные пары из столбца, используя 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.

Я не понимаю, как можно получить от 10 идентификаторов, начинающихся с G1, до пар типа (A0A147DQS2 │ A0A1X0GRM6) только путем создания «случайных пар этих идентификаторов белков». Откуда берутся эти новые идентификаторы? Это какое-то изменение в коде доступа к белкам при их объединении?

Thorsten Kettner 12.07.2024 07:31

@ThorstenKettner Ах да, это очень неясно из того, как я задал этот вопрос. В этой таблице очень много строк, и я показываю только первые 10. Когда вы шифруете, первые 10 представляют собой новый набор идентификаторов. Я внесу правку, чтобы было понятнее, спасибо.

Joseph Szymborski 12.07.2024 07:39
ReactJs | Supabase | Добавление данных в базу данных
ReactJs | Supabase | Добавление данных в базу данных
Это и есть ваш редактор таблиц в supabase.👇
Понимание Python и переход к SQL
Понимание Python и переход к SQL
Перед нами лабораторная работа по BloodOath:
3
2
82
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Я не знаю дакдб. Но это вызовет ошибку времени выполнения почти во всех СУБД:

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;

который может дать вам такой результат, как

КОД1 КОД2 А0А394ДПЛ7 А0А394ДПЛ7 С9ЖА8 С9ЖА8 А0А394ДПЛ7 С9ЖА8 С9ЖА8 А0А394ДПЛ7 ... ...
Ответ принят как подходящий

Есть несколько способов сделать это. Точный способ будет зависеть от ваших дополнительных требований.

Это очень просто: дважды возьмите случайную выборку, объедините их вместе. Вы можете получить один и тот же белок дважды, но можете исключить его с помощью дополнительного пункта 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 ссылаются. Это кажется странным, и кто знает, собираются ли они изменить это в каком-нибудь будущем выпуске.
Thorsten Kettner 12.07.2024 09:13

Да, вы также можете создать для этого 2 разных ctes :)

Roman Pekar 12.07.2024 09:32

Большое спасибо, второй ответ - это именно то, что я ищу :)

Joseph Szymborski 12.07.2024 19:25

Другие вопросы по теме