Вот родной подход Spark, который не затрудняет имена столбцов. Он основан на aggregateByKey
и использует словарь для сбора столбцов, которые отображаются для каждого ключа. Затем мы собираем все имена столбцов, чтобы создать окончательный файл данных. [Предварительная версия использовала jsonRDD после испускания словаря для каждой записи, но это более эффективно.] Ограничение на конкретный список столбцов или исключение таких, как XX
, было бы легкой модификацией.
Производительность кажется хорошим даже на довольно больших столах. Я использую вариацию, которая подсчитывает количество раз, каждое из которых имеет переменное число событий для каждого идентификатора, генерируя один столбец для каждого типа события. Код в основном тот же, за исключением того, что для подсчета вхождений используется коллекция.Counter вместо dict в seqFn
.
from pyspark.sql.types import *
rdd = sc.parallelize([('X01',41,'US',3),
('X01',41,'UK',1),
('X01',41,'CA',2),
('X02',72,'US',4),
('X02',72,'UK',6),
('X02',72,'CA',7),
('X02',72,'XX',8)])
schema = StructType([StructField('ID', StringType(), True),
StructField('Age', IntegerType(), True),
StructField('Country', StringType(), True),
StructField('Score', IntegerType(), True)])
df = sqlCtx.createDataFrame(rdd, schema)
def seqPivot(u, v):
if not u:
u = {}
u[v.Country] = v.Score
return u
def cmbPivot(u1, u2):
u1.update(u2)
return u1
pivot = (
df
.rdd
.keyBy(lambda row: row.ID)
.aggregateByKey(None, seqPivot, cmbPivot)
)
columns = (
pivot
.values()
.map(lambda u: set(u.keys()))
.reduce(lambda s,t: s.union(t))
)
result = sqlCtx.createDataFrame(
pivot
.map(lambda (k, u): [k] + [u.get(c) for c in columns]),
schema=StructType(
[StructField('ID', StringType())] +
[StructField(c, IntegerType()) for c in columns]
)
)
result.show()
Производит:
ID CA UK US XX
X02 7 6 4 8
X01 2 1 3 null
If I know that I'm going to need a function parametrized by some values and called repeatedly, I avoid globals by using a closure:
make.fn2 <- function(a, b) {
fn2 <- function(x) {
return( x + a + b )
}
return( fn2 )
}
a <- 2; b <- 3
fn2.1 <- make.fn2(a, b)
fn2.1(3) # 8
fn2.1(4) # 9
a <- 4
fn2.2 <- make.fn2(a, b)
fn2.2(3) # 10
fn2.1(3) # 8
This neatly avoids referencing global variables, instead using the enclosing environment of the function for a and b. Modification of globals a and b doesn't lead to unintended side effects when fn2 instances are called.
Есть причина, по которой некоторые языки не допускают использование глобальных переменных: они могут легко привести к поломке кода.
Правила области видимости в R позволяют писать код ленивым способом - позволяя функции, использующие переменные в других средах, могут сэкономить вам время на вводе текста и отлично подходят для экспериментов в простых случаях.
Если вы делаете что-то отдаленно сложное, то я рекомендую вам передать функции все переменные, которые ей нужны (или по крайней мере, необходимо провести тщательную проверку работоспособности, чтобы иметь запасной вариант на случай, если переменные не существуют).
В приведенном выше примере:
Лучшая практика - использовать fn1.
В качестве альтернативы, попробуйте что-нибудь вроде
fn3 <- function(x)
{
if(!exists("a", envir=.GlobalEnv))
{
warning("Variable 'a' does not exist in the global environment")
a <- 1
}
if(!exists("b", envir=.GlobalEnv))
{
warning("Variable 'b' does not exist in the global environment")
b <- 2
}
x + a + b
}
Использование глобальных переменных в большинстве языков не рекомендуется, и R не является исключением. Очень часто короткие функции используют короткие и общие имена переменных, которые могут быть заполнены в глобальной среде. Безопаснее всего: а) включить все переменные в определение функции б) , а не , чтобы назначать значения по умолчанию. Например, напишите f = function (a, b), а не f = function (a = 0, b = NA).
Возникает ли проблема, когда вы просто используете глобальную переменную в функции или когда вы пытаетесь присвоить переменную? Если это последнее, я подозреваю, что это потому, что вы не используете << -
в качестве присваивания внутри функции. И хотя использование << -
кажется темной стороной 1 , это может очень хорошо работать для ваших целей. В первом случае функция, вероятно, маскирует глобальную переменную.
Может помочь присвоение глобальным переменным имен таким образом, чтобы их было сложно замаскировать локально. например: global.pimultiples <- 1: 4 * pi