Power Query — региональные настройки

Я составил список лучших практик для своего отдела, который отлично работает на моем собственном компьютере (региональные настройки en-US), но когда я отправил его коллеге, все взорвалось (региональные настройки da-DK).

Половина отдела использует en-US, а другая половина использует da-DK. Не все могут использовать одно и то же, поэтому я хотел бы создать лист, который может обрабатывать оба варианта автоматически.

CSV-файлы, созданные в en-US, используют разделитель "," и "." для десятичного, а в настройках da-DK используется ";" в качестве разделителя и "," для десятичных знаков.

Как мне лучше подойти к этому вопросу?

У меня есть следующие два идентичных запроса данных

Данные1.csv:

    Panel/Node/Case,    MXX (kNm/m),    MYY (kNm/m)
    1/ 1/ 1,            145.46,         145.46  
    1/ 1/ 2,            98.83,          98.83   
    1/ 1/ 3 (C),        244.30,         244.30  
    1/ 2/ 1,            19.80,          19.80   
    1/ 2/ 2,            13.46,          13.46   
    1/ 2/ 3 (C),        33.26,          33.26   
    1/ 3/ 1,            19.80,          19.80   
    1/ 3/ 2,            13.46,          13.46   
    1/ 3/ 3 (C),        33.26,          33.26   
    1/ 4/ 1,            145.46,         145.46  
    1/ 4/ 2,            98.83,          98.83   
    1/ 4/ 3 (C),        244.30,         244.30

Данные2.csv:

    Panel/Node/Case;    MXX (kNm/m);    MYY (kNm/m)
    1/ 1/ 1;            145,46;         145,46
    1/ 1/ 2;            98,83;          98,83
    1/ 1/ 3 (C);        244,30;         244,30
    1/ 2/ 1;            19,80;          19,80
    1/ 2/ 2;            13,46;          13,46
    1/ 2/ 3 (C);        33,26;          33,26
    1/ 3/ 1;            19,80;          19,80
    1/ 3/ 2;            13,46;          13,46
    1/ 3/ 3 (C);        33,26;          33,26
    1/ 4/ 1;            145,46;         145,46
    1/ 4/ 2;            98,83;          98,83
    1/ 4/ 3 (C);        244,30;         244,30

Я рассматривал возможность использования функции замены значений для замены нужных мне символов, но, поскольку оба запроса содержат «,», это оказывается затруднительным.


person Martin    schedule 12.12.2019    source источник


Ответы (1)


Возможно, есть какие-то динамические/встроенные средства определения локали и разделителя содержимого файла. Я еще не нашел. Пока кто-нибудь не придет и не укажет лучший способ сделать это, возможно, приведенное ниже может послужить временным решением.

  • Функция parseCsv в приведенном ниже коде пытается вывести разделитель (предполагается, что разделитель всегда будет присутствовать в первой строке CSV сразу после Panel/Node/Case).
  • Он также пытается преобразовать значения последних двух столбцов из текста в числа (путем замены , на .). Если вы не хотите такого поведения, вы можете удалить его из кода. Кроме того, это преобразование будет работать только для локалей, которые используют . в качестве десятичного разделителя. (Если вам нужно поддерживать другие культуры/языки, возможно, имеет смысл попытаться определить культуру/язык и передать их в качестве второго аргумента Number.FromText.)

let
    data1 = Text.ToBinary(
    "Panel/Node/Case,    MXX (kNm/m),    MYY (kNm/m)
    1/ 1/ 1,            145.46,         145.46  
    1/ 1/ 2,            98.83,          98.83   
    1/ 1/ 3 (C),        244.30,         244.30  
    1/ 2/ 1,            19.80,          19.80   
    1/ 2/ 2,            13.46,          13.46   
    1/ 2/ 3 (C),        33.26,          33.26   
    1/ 3/ 1,            19.80,          19.80   
    1/ 3/ 2,            13.46,          13.46   
    1/ 3/ 3 (C),        33.26,          33.26   
    1/ 4/ 1,            145.46,         145.46  
    1/ 4/ 2,            98.83,          98.83   
    1/ 4/ 3 (C),        244.30,         244.30", TextEncoding.Utf8),

    data2 = Text.ToBinary(
    "Panel/Node/Case;    MXX (kNm/m);    MYY (kNm/m)
    1/ 1/ 1;            145,46;         145,46
    1/ 1/ 2;            98,83;          98,83
    1/ 1/ 3 (C);        244,30;         244,30
    1/ 2/ 1;            19,80;          19,80
    1/ 2/ 2;            13,46;          13,46
    1/ 2/ 3 (C);        33,26;          33,26
    1/ 3/ 1;            19,80;          19,80
    1/ 3/ 2;            13,46;          13,46
    1/ 3/ 3 (C);        33,26;          33,26
    1/ 4/ 1;            145,46;         145,46
    1/ 4/ 2;            98,83;          98,83
    1/ 4/ 3 (C);        244,30;         244,30", TextEncoding.Utf8),

    parseCsv = (someFile as binary) =>
        let
            lines = Lines.FromBinary(someFile, QuoteStyle.Csv, false, TextEncoding.Utf8),
            firstLine = List.First(lines),
            expectedDelimiterPosition = Text.Length("Panel/Node/Case"), 
            delimiterInferred = Text.At(firstLine, expectedDelimiterPosition),
            csv = Csv.Document(someFile, [Delimiter = delimiterInferred, Encoding = TextEncoding.Utf8, QuoteStyle = QuoteStyle.Csv]),
            promoted = Table.PromoteHeaders(csv, [PromoteAllScalars=true]),
            lastTwoColumnsAsNumbers = 
                let
                    lastTwoHeaders = List.LastN(Table.ColumnNames(promoted), 2),
                    replaceAndConvertToNumber = (someText as text) as number => Number.From(Text.Replace(someText, ",", ".")),
                    transformers = List.Transform(lastTwoHeaders, each {_, replaceAndConvertToNumber, type number}),
                    transformed = Table.TransformColumns(promoted, transformers)
                in transformed
        in lastTwoColumnsAsNumbers,
    parsed1 = parseCsv(data1),
    parsed2 = parseCsv(data2),
    parsed3 = parseCsv(File.Contents("C:\Users\MRCH\Desktop\Data1.csv"))
in
    parsed3

  • Чтобы реализовать это, вы можете скопировать приведенный выше код, создать пустой запрос (в моей версии Excel я делаю это через: Данные > Получить данные > Из других источников > Из пустого запроса), нажать Расширенный редактор (в левом верхнем углу) , удалите любой существующий код, вставьте то, что вы скопировали, затем нажмите «Готово».
  • Чтобы функция parseCsv работала с путем к файлу, вы можете, например, изменить parsed1 = parseCsv(data1) на parsed1 = parseCsv(File.Contents("SOME_FILE_PATH")), где SOME_FILE_PATH — это путь к файлу Data1.csv на вашем компьютере (сохраните двойные кавычки).
  • В редакторе запросов вы можете щелкнуть и просмотреть выражения/шаги parsed1 и parsed2 (которые в основном возвращают функция parseCsv для Data1.csv и Data2.csv соответственно). data1 и data2 предназначены только для демонстрационных целей, и вы должны заменить их фактическим двоичным содержимым ваших CSV.

Если это не поможет, дайте мне знать, где я могу улучшить свое объяснение.

person chillin    schedule 14.12.2019
comment
Большое спасибо за ответ! Это немного превышает мои возможности, поэтому я не уверен, как именно действовать дальше, поэтому рабочий пример был бы очень полезен! - person Martin; 16.12.2019
comment
@ Мартин, я переписал часть своего ответа, если хочешь еще раз взглянуть. - person chillin; 18.12.2019
comment
Спасибо, но главное, что я не понимаю, как импортировать данные? Обычно я бы использовал Source = Csv.Document(File.Contents(C:\Users\MRCH\Desktop\Data1.csv),[Delimiter=,, Columns=3, Encoding=1200, QuoteStyle=QuoteStyle.None]) Но как это переводится в то, что вы написали? Я хотел бы импортировать файл CSV, а вы определили его внутри запроса? - person Martin; 18.12.2019
comment
Попробуйте parseCsv(File.Contents("C:\Users\MRCH\Desktop\Data1.csv")), если у вас есть функция parseCsv, включенная в ваш код. В качестве примера добавлено выражение/шаг parsed3, чтобы вы могли увидеть, работает ли оно для ваших файлов или нет. - person chillin; 18.12.2019
comment
Фантастика, кажется, все работает. Большое спасибо за Вашу помощь! - person Martin; 19.12.2019