#python #database #sqlite #sqlalchemy #pylons
#python #База данных #sqlite #sqlalchemy #пилоны
Вопрос:
Я использую Python и Sqlalchemy для хранения значений широты и долготы в базе данных Sqlite. Я создал гибридный метод для моего объекта Location,
@hybrid_method
def great_circle_distance(self, other):
"""
Tries to calculate the great circle distance between the two locations
If it succeeds, it will return the great-circle distance
multiplied by 3959, which calculates the distance in miles.
If it cannot, it will return None.
"""
return math.acos( self.cos_rad_lat
* other.cos_rad_lat
* math.cos(self.rad_lng - other.rad_lng)
self.sin_rad_lat
* other.sin_rad_lat
) * 3959
Все значения, подобные cos_rad_lat
и sin_rad_lat
являющиеся значениями, которые я предварительно рассчитал для оптимизации вычисления. Во всяком случае, когда я запускаю следующий запрос,
pq = Session.query(model.Location).filter(model.Location.great_circle_distance(loc) < 10)
Я получаю следующую ошибку,
line 809, in great_circle_distance
* math.cos(self.rad_lng - other.rad_lng)
TypeError: a float is required
Когда я печатаю значения для self.rad_lng
и other.rad_lng
я получаю, например,
self.rad_lng: Location.rad_lng
other.rad_lng: -1.29154947064
Что я делаю не так?
Комментарии:
1. Это не формула хаверсина, это формула сферического закона косинусов. Смотрите (например) movable-type.co.uk/scripts/latlong.html
2. можем ли мы увидеть более полный пример вашего
Location
класса? В частности, как возникает каждое из этих свойств. Являются ли они атрибутами класса?3. Упс, ты прав. Не haversine, просто расстояние большого круга.
Ответ №1:
На самом деле вы не можете использовать math
модуль таким образом:
>>> c = toyschema.Contact()
>>> c.lat = 10
>>> c.lat
10
>>> import math
>>> math.cos(c.lat)
-0.83907152907645244
>>> math.cos(toyschema.Contact.lat)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: a float is required
sqalchemy.func.*
math.*
Для всего такого рода хитроумия у вас будет combine вместо in в @great_circle_distance.expression
методе. К сожалению, вы также не можете этого сделать с помощью sqlite; он не предоставляет тригонометрических функций
Вы могли бы использовать PostgreSQL, который работает, или вы можете попробовать добавить эти функции в sqlite самостоятельно:
РЕДАКТИРОВАТЬ На самом деле не сложно добавить функции в sqlite: это НЕ проверено.
Нужно добавить математические функции в sqlite:
engine = sqlalchemy.create_engine("sqlite:///:memory:/")
raw_con = engine.raw_connection()
raw_con.create_function("cos", 1, math.cos)
raw_con.create_function("acos", 1, math.acos)
class Location(...):
...
@hybrid_method
def great_circle_distance(self, other):
"""
Tries to calculate the great circle distance between
the two locations by using the Haversine formula.
If it succeeds, it will return the Haversine formula
multiplied by 3959, which calculates the distance in miles.
If it cannot, it will return None.
"""
return math.acos( self.cos_rad_lat
* other.cos_rad_lat
* math.cos(self.rad_lng - other.rad_lng)
self.sin_rad_lat
* other.sin_rad_lat
) * 3959
@great_circle_distance.expression
def great_circle_distance(cls, other):
return sqlalchemy.func.acos( cls.cos_rad_lat
* other.cos_rad_lat
* sqlalchemy.func.cos(cls.rad_lng - other.rad_lng)
cls.sin_rad_lat
* other.sin_rad_lat
) * 3959
Комментарии:
1. Спасибо! У меня было ощущение, что это связано с этим. Я попробую это позже вечером и дам вам знать!
2. Кроме того, еще одна вещь. Я заставил его работать, когда база данных sqlite находится в памяти, но для уже созданной я не могу добавить функции так, как вы описали. Как я могу добавить функции в базы данных sqlite, которые уже созданы?
3. Проблема не в том, что база данных уже создана, а в том, что функции доступны для каждого соединения; и sqlite создает совершенно новое соединение для баз данных на диске, чтобы избежать проблем потокобезопасности; Лучшее решение может зависеть от ваших потребностей в потоковой обработке; и поэтому вам следует посмотреть на параметры драйвера sqliteв sqlalchemy
4. Отлично. Спасибо за совет. В итоге я подключил прослушиватель событий к пулу, который создал пользовательские тригонометрические функции для каждого соединения: sqlalchemy.org/docs/core /…
Ответ №2:
Очевидно, что вы не можете получить значение с плавающей точкой из этой строки.
Это потому, что вы используете «self», который в качестве первого параметра вызова указывает, что метод является частью объекта, а не какой-то переменной, которую вы можете передать.
Вы должны попробовать это :
def great_circle_distance(self, first, other):
"""
Tries to calculate the great circle distance between
the two locations by using the Haversine formula.
If it succeeds, it will return the Haversine formula
multiplied by 3959, which calculates the distance in miles.
If it cannot, it will return None.
"""
return math.acos( self.cos_rad_lat
* other.cos_rad_lat
* math.cos(first.rad_lng - other.rad_lng)
self.sin_rad_lat
* other.sin_rad_lat
) * 3959
Я полагаю, что здесь выше глобальные переменные «self.cos_rad_lat» и «self.sin_rad_lat»
инициируются с правильными значениями где-то еще в вашей программе, вероятно, в разделе «init» того же объекта.
Комментарии:
1. Это не учитывает, как работает «гибридный» декоратор SQLAlchemy.
Ответ №3:
Похоже, вы все сделали правильно, но каким-то образом метод фактически не становится «гибридизированным». Не могли ли вы сделать что-нибудь глупое, например, фактически не включить декоратор в свой исходный код?
Комментарии:
1. Я импортировал с помощью этого: «из sqlalchemy.ext.hybrid импорт hybrid_property, hybrid_method»