Извлечение связанных данных из базы данных

У меня есть вопрос о Python и дизайне базы данных. Допустим, у меня есть большая база данных (табличная, как SQL) с множеством столбцов (функций) и миллионами строк (записей).

Я хотел бы извлечь каждую запись со многими функциями для совместной обработки.

База данных очень большая, например, содержит миллионы записей и десятки тысяч функций. Что я сейчас делаю, так это извлекаю каждый отдельный столбец в список, чтобы на каждую запись можно было ссылаться с одним и тем же индексом в разных списках. Есть лучший способ сделать это? Я думаю, будет ли фрейм данных лучше?

В качестве примера я обрабатываю 3 записи (на самом деле у меня миллионы записей), чтобы я мог отобразить их, как в операторе печати:

namelist = ['Peter', 'John', 'Susan']
agelist = [16, 17, 18]
activitylist = ['play tennis', 'play chess', 'swim']

for i, name in enumerate(namelist):
    print('Hi, my name is ' + name + '. I am ' + str(agelist[i]) + ' years old and I like to ' + activitylist[i])

Выход:

Hi, my name is Peter. I am 16 years old and I like to play tennis  
Hi, my name is John. I am 17 years old and I like to play chess  
Hi, my name is Susan. I am 18 years old and I like to swim

Кроме того, в вашем примере большую часть времени будет занимать printвывод миллионов строк в консоль, поэтому способ хранения данных менее актуален. Если бы вы могли записать вывод в файл или просто сохранить его в другой переменной, это было бы намного быстрее.

Stuart 12.04.2023 12:17

могу ли я узнать, как сделать векторизацию фрейма данных для примера конкатенации строк?

Sy.Yah 13.04.2023 07:45

Ответ @perpetualstudent показывает, как векторизовать

Stuart 13.04.2023 12:48
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
3
75
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Я бы использовал приведенные ниже коды настройки для уточнения своего ответа и для измерения времени. Обратите внимание, что в фрейме данных содержится 3 миллиона записей, и в качестве примера я выбрал операцию добавления этих данных к переменной.

import pandas as pd
import time

namelist = ['Peter', 'John', 'Susan'] *1000000
agelist = [16, 17, 18] *1000000
activitylist = ['play tennis', 'play chess', 'swim'] *1000000
df = pd.DataFrame({'name': namelist, 'age': agelist, 'activity': activitylist})

Ваш исходный метод уже очень эффективен, особенно если данные легко поступают в отдельные списки, а операция на моей машине занимает около 1,8 с:

start = time.time()
result = []
for i, name in enumerate(namelist):
    result.append('Hi, my name is ' + name + '. I am ' + str(agelist[i]) + ' years old and I like to ' + activitylist[i])
end = time.time()
print(end - start)

Выход:

1.7815442085266113

Позвольте мне разработать несколько альтернативных методов, если данные поступают в такой фрейм данных:

df = pd.DataFrame({'name': namelist, 'age': agelist, 'activity': activitylist})

Метод (1) с использованием df.iterrows()
Этот метод перебирает строку за строкой, и это очень медленно. В документации по итерации есть окно с предупреждением:

Итерация по объектам pandas обычно медленная. Во многих случаях повторение строк вручную не требуется...

В любом случае этот метод занимает около 112,3 с на моей машине:

start = time.time()
result = []
for i, row in df.iterrows():
    result.append('Hi, my name is ' + row['name'] + '. I am ' + str(row['age']) + ' years old and I like to ' + row['activity'])
end = time.time()
print(end - start)

Выход:

112.2983672618866

Метод (2) с использованием df.to_numpy()
Этот метод преобразует фрейм данных в массив numpy по строкам, а затем выполняет итерацию по каждому массиву, используя index. Это самое близкое к манипулированию списком, которое у вас было изначально. На моей машине это занимает около 2,7 с:

start = time.time()
result = []
for row in df.to_numpy():
    result.append('Hi, my name is ' + row[0] + '. I am ' + str(row[1]) + ' years old and I like to ' + row[2])
end = time.time()
print(end - start)

Выход:

2.7370002269744873

Метод (3) Векторизация
Невекторизованный метод (например, df.iterrows() или df.apply()) вызывает функцию Python для каждой строки, и эта функция Python выполняет дополнительные операции. Напротив, эта векторизованная операция намного быстрее, потому что она позволяет избежать использования кода Python во внутренних циклах. На моей машине это занимает около 1,9 с:

start = time.time()
df.age = df.age.astype('str')
df['result'] = 'Hi, my name is ' + df.name + '. I am ' + df.age + ' years old and I like to ' + df.activity
result = df.result.tolist()
end = time.time()
print(end - start)

Выход:

1.8785054683685303

Метод (4) Понимание списка с помощью Zip
Этот метод, предложенный @Stuart, кажется самым быстрым! На моей машине это заняло всего около 0,7 с:

start = time.time()
result = [f'Hi, my name is {name}. I am {age} years old and I like to {activity}'
           for name, age, activity in zip(namelist, agelist, activitylist)]
end = time.time()
print(end - start)

Выход:

0.7034788322448731

Спасибо, я бы использовал метод (3) векторизации, поскольку исходные данные поступают в табличном формате!

Sy.Yah 30.05.2023 10:11

Кажется, самый быстрый способ сделать это — объединить списки вместе, а затем использовать понимание списка с форматированной строкой:

result = [f'Hi, my name is {name}. I am {age} years old and I like to {activity}'
           for name, age, activity in zip(namelist, agelist, activitylist)]

Метод векторизованных панд (как показано в ответе @perpetualstudent) кажется немного медленнее, чем этот, даже если вы можете хранить age в виде строк, чтобы преобразование строк не требовалось. Очевидно, это связано с тем, что строковые операции нельзя ускорить с помощью векторизации в той же степени, что и математические операции в пандах.

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