#string #stata #local-variables #stata-macros
Вопрос:
Учитывая локальный макрос, содержащий строку уровней, разделенных запятой ( » ,») или запятой и пробелом ( » ,») или даже только пробелом ( «» ), существует ли простой способ извлечь первые N уровней (или слов) этого локального макроса?
Строка будет выглядеть как "12, 123, 1321, 41"
, или "12,123,1321,41"
или "12 123 1321 41"
.
В принципе, я был бы доволен версией макрофункции word # of string
, которая работала бы более или менее похоже word 1/N of string
. (См. «Макрофункции для синтаксического анализа» на стр. 12 в разделе Определение и управление макросами)
Для большего контекста я работаю с выводом levelsof, local() sep()
. Таким образом, я могу выбрать разделитель, с которым будет легче работать. Я хочу передать полученные уровни в качестве аргумента inlist()
функции. Обычно работает следующее, но inlist()
требуется всего до 250 аргументов. Вот почему я хотел бы извлечь фрагменты из 250 слов результатов levelsof()
sysuse auto, clear
levelsof mpg if trunk > 20, local(levels) sep(", ")
list if inlist(mpg, `levels')
«решение» до сих пор
Я придумал не простой способ добиться этого, но он выглядит не очень хорошо, и мне интересно, есть ли простой встроенный способ сделать то же самое.
sysuse auto, clear
levelsof mpg if trunk > 20, local(levels) sep(", ")
scalar number_of_words = 3
forvalues i = 1 (1) `=number_of_words' {
local word_i = `i'
local this_level : word `word_i' of `levels'
local list_of_levels = "`list_of_levels'`this_level'"
di as text "loop: `i'"
di as text "this level: `this_level'"
di as text "list of levels so far: `list_of_levels'"
}
di "`list_of_levels'"
// trim trailing comma
local trimmed_list_of_levels = substr( "`list_of_levels'" , 1 , strlen( "`list_of_levels'" )-1)
di "`trimmed_list_of_levels'"
list make mpg price trunk if inlist(mpg, `trimmed_list_of_levels')
выход
. sysuse auto, clear
(1978 Automobile Data)
.
. levelsof mpg if trunk > 20, local(levels) sep(", ")
12, 15, 17, 18
. scalar number_of_words = 3
. forvalues i = 1 (1) `=number_of_words' {
2. local word_i = `i'
3. local this_level : word `word_i' of `levels'
4. local list_of_levels = "`list_of_levels'`this_level'"
5.
. di as text "loop: `i'"
6. di as text "this level: `this_level'"
7. di as text "list of levels so far: `list_of_levels'"
8. }
loop: 1
this level: 12,
list of levels so far: 12,
loop: 2
this level: 15,
list of levels so far: 12,15,
loop: 3
this level: 17,
list of levels so far: 12,15,17,
.
. di "`list_of_levels'"
12,15,17,
.
. // trim trailing comma
. local trimmed_list_of_levels = substr( "`list_of_levels'" , 1 , strlen( "`list_of_levels'" )-1)
.
. di "`trimmed_list_of_levels'"
12,15,17
. list make mpg price trunk if inlist(mpg, `trimmed_list_of_levels')
------------------------------------------
| make mpg price trunk |
|------------------------------------------|
2. | AMC Pacer 17 4,749 11 |
5. | Buick Electra 15 7,827 20 |
23. | Dodge St. Regis 17 6,342 21 |
26. | Linc. Continental 12 11,497 22 |
27. | Linc. Mark V 12 13,594 18 |
|------------------------------------------|
31. | Merc. Marquis 15 6,165 23 |
53. | Audi 5000 17 9,690 15 |
74. | Volvo 260 17 11,995 14 |
------------------------------------------
правки, касающиеся комментариев.
правка 01)
Например, следующее не работает. Он возвращает ошибку 130 expression too long
.
clear
set obs 1000
gen id = _n
gen x1 = rnormal()
sum *
levelsof id if x1>0, local(levels) sep(", ")
sum * if inlist(id, `levels')
пример, где эта конструкция (levelsof inlist) кажется необходимой
clear
set obs 5000
gen id = round(_n/5)
gen x1 = rnormal()
sum *
levelsof id if x1>2, local(levels) sep(", ")
sum * if x1>2 // if threshold is small enough, there will be too many values for inlist()
sum * if inlist(id, `levels')
Комментарии:
1. Вы можете использовать
word
команду в цикле, гдеword
будет возвращено n-е слово в списке (см.help word
). Можете ли вы подробнее рассказать о том, каков ваш окончательный план на будущееinlist
? Возможно, есть более простой способ сделать это.2. Спасибо за быстрый ответ. См. правку 1 в вопросе. если существует более (около) 250 уровней, функция inlist возвращает
error 130``expression too long
значение . Когда их меньше, это работает нормально.word
Функция извлекает только одно слово из строки. Я блуждал, есть ли встроенный модуль для извлеченияn
первого слова s , который позволит избежать использования этого цикла, который я написал в вопросе. Однако цикл работает, если нет встроенного или более простого способа.3. Быстрый ответ: Вставьте локальный в Mata и используйте результат
tokens()
.4. Ответ Ника лучше всего подходит для вашего вопроса. С моей точки зрения, трудно увидеть конечную цель, которая потребовала бы этого
sum * if x1>0
в отношении вашего редактирования 1.5. Правда, я добавлю пример, где имеет значение пройти этот маршрут.. Представьте , что у меня есть несколько наблюдений одного и того же
id
, не обязательно с одним и тем жеx1
. Если я хочу извлечь все наблюдения (предыдущие и прошлые) из техid
s, которые хотя бы однажды имелиx1
значение больше порогового значения , то, насколько мне известно, мне нужно будет прибегнуть к этойlevelsof
inlist
конструкции .
Ответ №1:
Используя ваш дополнительный пример в качестве основы, вы могли бы использовать egen max
для создания флага, равного 1 для всего id
, у которого есть любые случаи, когда x1
значение превышает определенный порог. Например:
clear
set seed 2021
set obs 5000
gen id = round(_n/5)
gen x1 = rnormal()
sum *
levelsof id if x1>2, local(levels) sep(", ")
sum * if x1>2 // if threshold is small enough, there will be too many values for inlist()
sum * if inlist(id, `levels')
//This will do the same thing
gen over_threshold = x1>2
egen id_over_thresh = max(over_threshold), by(id)
sum * if id_over_thresh
Комментарии:
1. Для использования в будущем вы можете сократить его до
egen id_over_thresh = max(x1>2), by(id)
2. очень интересная идея! Я посмотрю, применимо ли это к моим фактическим случаям использования, которые немного сложнее, и операторы if основаны на нескольких переменных и условиях… (включая числовые и категориальные данные…). В любом случае, хотя это может даже решить мою проблему, оно не адресует вопрос напрямую…
3. Обратите внимание, что здесь
x1
будет >2, если оно отсутствует.4. Да, Ник правильно указал на пропажи. Я согласен, что это не дает прямого ответа на ваш вопрос в том виде, в каком он есть, нажатие на
mata
это-лучший короткий путь, который я могу придумать. Приведенный выше ответ должен быть достаточно хорошо обобщен, независимо от сложности условий или любого количества идентифицирующих переменных вby
группе.5. Спасибо JR96. Он действительно работает с моими фактическими данными, и ваше решение очень гибкое. Довольно простое, но элегантное решение моей проблемы…