Это связано с требованием отдельной компиляции и потому, что шаблоны являются полиморфизмом типа создания экземпляров.
Давайте немного приблизимся к конкретному для объяснения. Скажем, у меня есть следующие файлы:
class MyClass<T>
class MyClass<T>
MyClass<int>
. Отдельное средство компиляции. Я должен скомпилировать foo.cpp независимо от bar.cpp. Компилятор полностью выполняет всю сложную работу по анализу, оптимизации и генерации кода на каждом модуле компиляции; нам не нужно анализировать целую программу. Только компоновщик должен обрабатывать всю программу одновременно, и задача компоновщика значительно упрощается.
bar.cpp даже не нужно существовать при компиляции foo.cpp, но я все равно должен быть в состоянии связать foo.o Я уже имел вместе с bar.o Я только что выпустил, не перекомпилируя foo.cpp. foo.cpp может даже быть скомпилирован в динамическую библиотеку, распределенную где-то в другом месте без foo.cpp, и связан с кодом, который они пишут спустя годы после того, как я написал foo.cpp.
«Полиморфизм в стиле объектов» означает, что template MyClass<T>
не является общим классом, который может быть скомпилирован в код, который может работать для любого значения T
. Это добавит накладные расходы, такие как бокс, необходимо передать указатели на функции для распределителей и конструкторов и т. Д. Намерение шаблонов C ++ состоит в том, чтобы избежать необходимости писать почти идентичные class MyClass_int
, class MyClass_float
и т. Д., Но все же быть в состоянии закончить с компилируемым кодом, который в основном выглядит так, как если бы мы имели каждую версию отдельно. Таким образом, шаблон является буквально шаблоном; шаблон класса не класс, это рецепт создания нового класса для каждого T
, с которым мы сталкиваемся. Шаблон не может быть скомпилирован в код, только результат создания экземпляра шаблона может быть скомпилирован.
Итак, когда foo.cpp скомпилирован, компилятор не может видеть bar.cpp, чтобы знать, что MyClass<int>
необходимо. Он может видеть шаблон MyClass<T>
, но он не может испускать код для этого (это шаблон, а не класс). И когда компилируется bar.cpp, компилятор может видеть, что ему нужно создать MyClass<int>
, но он не может видеть шаблон MyClass<T>
(только его интерфейс в foo.h), поэтому он не может его создать.
Если foo.cpp сам использует MyClass<int>
, тогда код для него будет сгенерирован при компиляции foo.cpp, поэтому, когда bar.o связан с foo.o, они могут быть подключены и будут работать. Мы можем использовать этот факт, чтобы позволить конечный набор экземпляров шаблонов быть реализован в .cpp-файле, написав один шаблон. Но bar.cpp не может использовать шаблон в качестве шаблона и создавать его на всех типах, которые ему нравятся; он может использовать только ранее существовавшие версии шаблона, которые автор foo.cpp думал предоставить.
Вы можете подумать, что при компиляции шаблона компилятор должен «сгенерировать все версии», с теми, которые никогда не используются, отфильтровываются во время связывания. Помимо огромных накладных расходов и экстремальных трудностей, с которыми сталкивался такой подход, поскольку «модификаторы типа», такие как указатели и массивы, позволяют даже встроенным типам создавать бесконечное количество типов, что происходит, когда я расширяю свою программу добавив:
class BazPrivate
и использует MyClass<BazPrivate>
Невозможно, чтобы это могло работать, если мы либо
MyClass<T>
MyClass<T>
, чтобы компилятор мог генерировать MyClass<BazPrivate>
во время компиляции baz.cpp. Никто не любит (1), потому что системы компиляции целых программ принимают forever для компиляции и потому что это делает невозможным распространение компилированных библиотек без исходного кода. Итак, у нас есть (2).
Вот подход groupby
и apply
:
import pandas as pd
import numpy as np
def weighted_average(group):
return (group["value A"] * group["value B"]).sum() / group["value B"].sum()
df = pd.DataFrame({"value A": np.random.randint(1, 100, 10),
"value B": np.random.randint(1, 100, 10),
"country": np.random.choice(["US", "UK"], 10),
"category": np.random.choice(["Red", "Green"], 10)},
index=pd.date_range("2018-01-26", "2018-02-04", num=10))
print(df)
# category country value A value B
# 2018-01-26 Green UK 74 93
# 2018-01-27 Green UK 57 1
# 2018-01-28 Green US 6 24
# 2018-01-29 Green UK 31 89
# 2018-01-30 Green UK 73 75
# 2018-01-31 Green US 86 63
# 2018-02-01 Green US 86 30
# 2018-02-02 Green US 53 37
# 2018-02-03 Red UK 50 69
# 2018-02-04 Red US 98 33
print(df.groupby([pd.Grouper(freq='M'), "country"]).apply(weighted_average)).unstack()
# country UK US
# 2018-01-31 58.810078 63.931034
# 2018-02-28 50.000000 77.750000
Обратите внимание на pandas.Grouper
, который группируется по месяцам (по умолчанию по индексу, но вы также можете указать столбец с помощью key="date"
, если вы не хотите устанавливать индекс).
Если вы также хотите разделить эту категорию по этой теме одновременно, вы можете просто добавить ее к вызову groupby (df.groupby([pd.Grouper(freq='M'), "country", "category"])...
). Это сделает индекс на один уровень глубже, поэтому вам нужно решить, хотите ли вы использовать мультииндекс для столбцов или строк. Если вы хотите его на столбцах, просто добавьте еще один вызов к unstack()
в конце.
Я бы создал новый столбец mmyy - если столбец даты является датой или строкой, вам может потребоваться использовать модуль datetime. Затем группируйте по mmyy и получите общую сумму A и B, а затем создайте новый столбец, который является просто A / B
numpy
иpandas
вы используете? Являются ли ваши даты столбцами даты или текста (вы можете конвертировать их с помощьюpandas.to_datetime
, если это так). Является ли пример, который я опубликовал, не работает для вас или внутри вашего кода? Вы действительно установили дату как индекс (сdf = df.set_index("date")
) или передали его группе сpd.Grouper(key="date", freq="M")
? – Graipher 13 July 2018 в 21:11functools.partial
или сделать функцию функцией, которая возвращает другую функцию. Или, как последняя солома, используйте глобальные переменные. – Graipher 14 July 2018 в 07:37