При левом соединении для отображения D3 возвращается ноль, что препятствует следующему шагу командной строки обработки данных (ndjson-join или jq)
Я делаю D3 картографирование на государственном уровне. Вот одна проблема, с которой я столкнулся при обработке данных. Например, данные карты такие, (dat1.ndjson)
{state: a, code: aa}
{state: b, code: bb}
{state: c, code: cc}
Но обычно информация, которой мы располагаем, не является полной, например, в Антарктике информации обычно нет, но нам все равно нужно очерчивать ее контур, когда мы проводим картографирование. Информационные данные как, (dat2.ndjson)
{state: a, code: aa, count: 1}
{state: b, code: bb, count: 2}
Таким образом, когда я пытаюсь выполнить левое соединение с этими двумя данными, оно возвращает (dat3.ndjson)
[{state: a, code: aa},{state: a, code: aa, count: 1}]
[{state: b, code: bb},{state: b, code: bb, count: 2}]
[{state: c, code: cc},null]
Это возвращается
ndjson-join --left 'd.code' dat1.ndjson dat2.ndjson < merge.ndjson
Цель состоит в том, чтобы связать эту информацию 'count' с данными карты, поэтому обычно я сначала назначаю всем элементам count = 0 в dat1.ndjson, например, (dat11.ndjson)
{state: a, code: aa, count: 0}
{state: b, code: bb, count: 0}
{state: c, code: cc, count: 0}
и затем используйте этот метод левого соединения, подобный тому, который я показал ранее, чтобы получить что-то вроде этого (dat33.ndjson)
[{state: a, code: aa, count: 0},{state: a, code: aa, count: 1}]
[{state: b, code: bb, count: 0},{state: b, code: bb, count: 2}]
[{state: c, code: cc, count: 0},null]
Но тут возникает проблема. Если я использую следующую команду, чтобы сложить все значения вместе, она вернет ошибку из-за этого нуля в третьей строке.
ndjson-map '{state: d[0].state, code: d[0].code, count: d[0].count +
d[1].count}' < dat33.ndjson > merge.ndjson
Теперь я должен выполнить эту обработку данных в R, которая занимает много времени, так как мне нужно сделать преобразование между.ndjson и.csv. Поэтому я ищу лучший способ сделать это. Я думаю, что могут быть какие-то способы с использованием "ndjson-cli", "jq" или "awk", "sed" и т. Д.
У кого-нибудь есть идеи? Спасибо!:)
E.
1 ответ
Вот решение, которое состоит из нескольких частей:
- Преобразование вашего ввода в действительный JSON.
- Библиотечные функции для выполнения объединений.
- Запуск jq для получения желаемого результата при условии, что ваша версия jq достаточно свежая.
- Что делать, если у вас есть доступ только к jq 1.5
Чтобы проиллюстрировать, насколько все просто, как только вы решите проблемы с пухом, вот "основная" программа jq:
join(.state) | .count //= 0
По сути, это говорит: выполните соединение, используя.state в качестве ключа соединения, а затем убедитесь, что установлено.count.
Выход из вышеупомянутой однострочной строки будет NDJSON:
{"state":"a","code":"aa","count":1}
{"state":"b","code":"bb","count":2}
{"state":"c","code":"cc","count":0}
Часть 1: dat1.json и dat2.json
Я собираюсь предположить, что вы можете создать действительный JSON из ваших входных данных. Для выборочных данных я использовал sed
:
for i in 1 2 ; do
sed -e 's/state/"state"/' -e 's/code/"code"/' -e 's/count/"count"/' \
-e 's/ \([a-z]*\)\([,}]\)/ "\1"\2/g' dat$i.ndjson > dat$i.json
done
В любом случае, нижеприведенное предполагает, что у вас есть два файла, dat1.json и dat2.json, которые содержат потоки действительного JSON.
Часть 2: join
Вот небольшая библиотека фильтров для создания объединений: первый работает с потоками, а остальные с массивами. Эти определения предполагают, что ваш JQ имеет INDEX/2
, Смотрите часть 4, если это не так.
def joins(s1; s2; filter1; filter2):
# combine two dictionaries using `add`
def merge: . as $in
| reduce (add|keys_unsorted[]) as $k ({}; .[$k] = ([$in[] | .[$k]] | add));
[INDEX(s1; filter1 | select(. != null)), INDEX(s2; filter2 | select(. != null))]
| merge[] ;
def join(filter1; filter2):
joins(.[0][]; .[1][]; filter1; filter2);
def join(f): join(f; f);
Часть 3. Решение
Во-первых, давайте будем проще. Если вы поместите приведенные выше определения для join
а также joins
в файле, скажем, d3.jq, за которым следует однострочная программа, указанная в преамбуле, тогда следующий вызов поможет, если ваш jq имеет INDEX
:
jq -c -s -f d3.jq <(jq -s . dat1.json) <(jq -s . dat2.json)
Это предполагает, что вы используете оболочку, которая поддерживает подстановку процессов. Если нет, то вы можете сначала запустить "." программы отдельно, например, если у вас есть sponge
:
for i in 1 2 ; do jq -s . dat$i.json | sponge dat$i.json ; done
С помощью include
Если ваш JQ поддерживает include
и если у вас есть вышеуказанные определения join
в частной стандартной библиотеке, такой как ~/.jq/jq/jq.jq, ваша основная jq-программа становится двухслойной:
include "jq";
join(.state) | .count //= 0'
Это означает, что вы можете обойтись без d3.jq и использовать вызов:
jq -c -s 'include "jq"; join(.state)|.count //= 0' \
<(jq -s . dat1.json) <(jq -s . dat2.json)
Часть 4: INDEX
Вот копия INDEX
как предусмотрено последними версиями JQ. Вы можете добавить эти определения в d3.jq (перед "основной" частью программы), или в файл вашей библиотеки, и так далее:
def INDEX(stream; idx_expr):
reduce stream as $row ({};
.[$row|idx_expr|
if type != "string" then tojson
else .
end] |= $row);
def INDEX(idx_expr): INDEX(.[]; idx_expr);