Способ получить количество цифр в интервале?

В зависимости от вашего формата вы можете использовать ast.literal_eval . Лучше попытайтесь решить проблему до до того, как ваш фрейм данных будет создан.

from ast import literal_eval

df = pd.DataFrame({'df_test': ['(-, 136), (-, 136), 1.0',
                               '(-, 136), (-, 438), 0.5',
                               '(-, 136), (-, 257), 0.5']})

series = df.pop('df_test').str.replace('-', '"-"').apply(literal_eval)
df = df.join(pd.DataFrame(series.values.tolist(), columns=['df_t1', 'df_t2', 'df_val']))

print(df)

      df_t1     df_t2  df_val
0  (-, 136)  (-, 136)     1.0
1  (-, 136)  (-, 438)     0.5
2  (-, 136)  (-, 257)     0.5

370
задан rawplutonium 11 February 2019 в 08:28
поделиться

8 ответов

Ваше решение на основе String совершенно нормально, в нем нет ничего «неуместного». Вы должны понимать, что математически числа не имеют ни длины, ни цифр. Длина и цифры являются свойствами физического представления числа в определенной базе, то есть в строке.

Решение на основе логарифма делает (частично) те же вещи, что и решение на основе строк. внутренне, и, вероятно, делает это (незначительно) быстрее, потому что он производит только длину и игнорирует цифры. Но на самом деле я бы не стал считать это более ясным в намерениях - а это самый важный фактор.

Вы должны понимать, что математически числа не имеют ни длины, ни цифр. Длина и цифры являются свойствами физического представления числа в определенной базе, то есть в строке.

Решение на основе логарифма делает (частично) те же вещи, что и решение на основе строк. внутренне, и, вероятно, делает это (незначительно) быстрее, потому что он производит только длину и игнорирует цифры. Но на самом деле я бы не стал считать это более ясным в намерениях - а это самый важный фактор.

Вы должны понимать, что математически числа не имеют ни длины, ни цифр. Длина и цифры являются свойствами физического представления числа в определенной базе, то есть в строке.

Решение на основе логарифма делает (частично) те же вещи, что и решение на основе строк. внутренне, и, вероятно, делает это (незначительно) быстрее, потому что он производит только длину и игнорирует цифры. Но на самом деле я бы не стал считать это более ясным в намерениях - а это самый важный фактор.

и, вероятно, делает это (незначительно) быстрее, потому что он выдает только длину и игнорирует цифры. Но на самом деле я бы не стал считать это более ясным в намерениях - а это самый важный фактор.

и, вероятно, делает это (незначительно) быстрее, потому что он выдает только длину и игнорирует цифры. Но на самом деле я бы не стал считать это более ясным в намерениях - а это самый важный фактор.

332
ответ дан 23 November 2019 в 00:06
поделиться

Я записал эту функцию после взгляда Integer.java исходный код.

private static int stringSize(int x) {
    final int[] sizeTable = {9, 99, 999, 9_999, 99_999, 999_999, 9_999_999,
            99_999_999, 999_999_999, Integer.MAX_VALUE};
    for (int i = 0; ; ++i) {
        if (x <= sizeTable[i]) {
            return i + 1;
        }
    }
}
0
ответ дан 23 November 2019 в 00:06
поделиться

Можно попробовать? ;)

на основе решения Дирка

final int digits = number==0?1:(1 + (int)Math.floor(Math.log10(Math.abs(number))));
3
ответ дан 23 November 2019 в 00:06
поделиться

Логарифм - ваш друг:

int n = 1000;
int length = (int)(Math.log10(n)+1);

NB: действительно только для n> 0.

255
ответ дан 23 November 2019 в 00:06
поделиться

Поскольку количество цифр в базе 10 целого числа равно 1 + усечение (log10 (число)) , вы можете сделать:

public class Test {

    public static void main(String[] args) {

        final int number = 1234;
        final int digits = 1 + (int)Math.floor(Math.log10(number));

        System.out.println(digits);
    }
}

Отредактировано потому что в моем последнем редактировании был исправлен пример кода, но не описание.

8
ответ дан 23 November 2019 в 00:06
поделиться

Любопытно, я попытался протестировать его ...

import org.junit.Test;
import static org.junit.Assert.*;


public class TestStack1306727 {

    @Test
    public void bench(){
        int number=1000;
        int a= String.valueOf(number).length();
        int b= 1 + (int)Math.floor(Math.log10(number));

        assertEquals(a,b);
        int i=0;
        int s=0;
        long startTime = System.currentTimeMillis();
        for(i=0, s=0; i< 100000000; i++){
            a= String.valueOf(number).length();
            s+=a;
        }
        long stopTime = System.currentTimeMillis();
        long runTime = stopTime - startTime;
        System.out.println("Run time 1: " + runTime);
        System.out.println("s: "+s);
        startTime = System.currentTimeMillis();
        for(i=0,s=0; i< 100000000; i++){
            b= number==0?1:(1 + (int)Math.floor(Math.log10(Math.abs(number))));
            s+=b;
        }
        stopTime = System.currentTimeMillis();
        runTime = stopTime - startTime;
        System.out.println("Run time 2: " + runTime);
        System.out.println("s: "+s);
        assertEquals(a,b);


    }
}

результаты следующие:

Run time 1: 6765
s: 400000000
Run time 2: 6000
s: 400000000

Теперь мне остается задаться вопросом, действительно ли мой тест что-то значит, но я получаю стабильные результаты (варианты за мс) за несколько прогонов самого теста ... :) Похоже, бесполезно пытаться оптимизировать это ...


edit: следуя комментарию ptomli, я заменил 'number' на 'i' в коде выше и получил следующие результаты за 5 прогонов стенда:

Run time 1: 11500
s: 788888890
Run time 2: 8547
s: 788888890

Run time 1: 11485
s: 788888890
Run time 2: 8547
s: 788888890

Run time 1: 11469
s: 788888890
Run time 2: 8547
s: 788888890

Run time 1: 11500
s: 788888890
Run time 2: 8547
s: 788888890

Run time 1: 11484
s: 788888890
Run time 2: 8547
s: 788888890
1
ответ дан 23 November 2019 в 00:06
поделиться

Два комментария к вашему тесту: Java - сложная среда, что с своевременной компиляцией, сборкой мусора и т. д., поэтому, чтобы получить честное сравнение, всякий раз, когда я запускаю тест, я всегда: (а) заключаю два теста в цикл, который запускает их последовательно 5 или 10 раз . Довольно часто время выполнения при втором проходе цикла сильно отличается от первого. И (б) После каждого «подхода» я выполняю System.gc (), чтобы попытаться запустить сборку мусора. В противном случае при первом подходе может быть сгенерирована группа объектов, но этого недостаточно для принудительной сборки мусора, затем при втором подходе создается несколько объектов, куча исчерпывается и выполняется сборка мусора. Тогда второй подход «взимается» за сбор мусора, оставленного первым подходом. Очень несправедливо!

Тем не менее, ни одно из вышеперечисленных не имело существенных различий в этом примере.

С этими модификациями или без них я получил совсем другие результаты, чем вы. Когда я запускал это, да, подход toString давал время выполнения от 6400 до 6600 миллисекунд, а подход журнала - от 20 000 до 20 400 миллисекунд. Вместо того, чтобы быть немного быстрее, подход журнала был для меня в 3 раза медленнее.

Обратите внимание, что эти два подхода связаны с очень разными затратами, так что это не совсем шокирует: подход toString создаст множество временных объектов, которые должны быть быть очищенным, в то время как подход журнала требует более интенсивных вычислений. Так что, возможно, разница в том, что на машине с меньшим объемом памяти toString требует большего количества циклов сборки мусора, тогда как на машине с более медленным процессором дополнительное вычисление журнала будет более болезненным.

Я также попробовал третий подход. Я написал эту маленькую функцию:

static int numlength(int n)
{
    if (n == 0) return 1;
    int l;
    n=Math.abs(n);
    for (l=0;n>0;++l)
        n/=10;
    return l;           
}

Это работало с 1600 по 1900 миллисекунд - менее 1/3 подхода toString и 1/10 подхода журнала на моей машине.

Если бы у вас был широкий диапазон чисел, вы могли бы ускорить его далее, начиная с деления на 1 000 или 1 000 000, чтобы уменьшить количество проходов цикла. Я не играл с этим.

13
ответ дан 23 November 2019 в 00:06
поделиться

Самый быстрый подход: разделяй и властвуй.

Предполагая, что ваш диапазон составляет от 0 до MAX_INT, у вас есть от 1 до 10 цифр. Вы можете приблизиться к этому интервалу, используя «разделяй и властвуй», до 4 сравнений на каждый вход. Сначала вы делите [1..10] на [1..5] и [6..10] с одним сравнением, а затем каждый интервал длиной 5 делите с помощью одного сравнения на один интервал длины 3 и один интервал длины 2. Интервал длиной 2 требует еще одного сравнения (всего 3 сравнения), интервал длины 3 можно разделить на интервал длины 1 (решение) и интервал длины 2. Итак, вам нужно 3 или 4 сравнения.

Никаких делений, никаких операций с плавающей запятой, никаких дорогостоящих логарифмов, только целочисленные сравнения.

Код (длинный, но быстрый):

if (n < 100000){
        // 5 or less
        if (n < 100){
            // 1 or 2
            if (n < 10)
                return 1;
            else
                return 2;
        }else{
            // 3 or 4 or 5
            if (n < 1000)
                return 3;
            else{
                // 4 or 5
                if (n < 10000)
                    return 4;
                else
                    return 5;
            }
        }
    } else {
        // 6 or more
        if (n < 10000000) {
            // 6 or 7
            if (n < 1000000)
                return 6;
            else
                return 7;
        } else {
            // 8 to 10
            if (n < 100000000)
                return 8;
            else {
                // 9 or 10
                if (n < 1000000000)
                    return 9;
                else
                    return 10;
            }
        }
    }

Тест (после прогрева JVM) - см. код ниже, чтобы увидеть, как был запущен тест:

  1. базовый метод (с String.length): 2145 мс
  2. метод log10: 711 мс = 3,02 раза быстрее, чем базовый
  3. повторное деление: 2797 мс = 0,77 раза быстрее, чем базовый уровень
  4. разделяй и властвуй: 74 мс = 28,99
    раз быстрее, чем базовый

Полный код:

public static void main(String[] args)
throws Exception
{

    // validate methods:
    for (int i = 0; i < 1000; i++)
        if (method1(i) != method2(i))
            System.out.println(i);
    for (int i = 0; i < 1000; i++)
        if (method1(i) != method3(i))
            System.out.println(i + " " + method1(i) + " " + method3(i));
    for (int i = 333; i < 2000000000; i += 1000)
        if (method1(i) != method3(i))
            System.out.println(i + " " + method1(i) + " " + method3(i));
    for (int i = 0; i < 1000; i++)
        if (method1(i) != method4(i))
            System.out.println(i + " " + method1(i) + " " + method4(i));
    for (int i = 333; i < 2000000000; i += 1000)
        if (method1(i) != method4(i))
            System.out.println(i + " " + method1(i) + " " + method4(i));

    // work-up the JVM - make sure everything will be run in hot-spot mode
    allMethod1();
    allMethod2();
    allMethod3();
    allMethod4();

    // run benchmark
    Chronometer c;

    c = new Chronometer(true);
    allMethod1();
    c.stop();
    long baseline = c.getValue();
    System.out.println(c);

    c = new Chronometer(true);
    allMethod2();
    c.stop();
    System.out.println(c + " = " + StringTools.formatDouble((double)baseline / c.getValue() , "0.00") + " times faster than baseline");

    c = new Chronometer(true);
    allMethod3();
    c.stop();
    System.out.println(c + " = " + StringTools.formatDouble((double)baseline / c.getValue() , "0.00") + " times faster than baseline");

    c = new Chronometer(true);
    allMethod4();
    c.stop();
    System.out.println(c + " = " + StringTools.formatDouble((double)baseline / c.getValue() , "0.00") + " times faster than baseline");
}


private static int method1(int n)
{
    return Integer.toString(n).length();
}
private static int method2(int n)
{
    if (n == 0)
        return 1;
    return (int)(Math.log10(n) + 1);
}
private static int method3(int n)
{
    if (n == 0)
        return 1;
    int l;
    for (l = 0 ; n > 0 ;++l)
        n /= 10;
    return l;
}
private static int method4(int n)
{
    if (n < 100000)
    {
        // 5 or less
        if (n < 100)
        {
            // 1 or 2
            if (n < 10)
                return 1;
            else
                return 2;
        }
        else
        {
            // 3 or 4 or 5
            if (n < 1000)
                return 3;
            else
            {
                // 4 or 5
                if (n < 10000)
                    return 4;
                else
                    return 5;
            }
        }
    }
    else
    {
        // 6 or more
        if (n < 10000000)
        {
            // 6 or 7
            if (n < 1000000)
                return 6;
            else
                return 7;
        }
        else
        {
            // 8 to 10
            if (n < 100000000)
                return 8;
            else
            {
                // 9 or 10
                if (n < 1000000000)
                    return 9;
                else
                    return 10;
            }
        }
    }
}


private static int allMethod1()
{
    int x = 0;
    for (int i = 0; i < 1000; i++)
        x = method1(i);
    for (int i = 1000; i < 100000; i += 10)
        x = method1(i);
    for (int i = 100000; i < 1000000; i += 100)
        x = method1(i);
    for (int i = 1000000; i < 2000000000; i += 200)
        x = method1(i);

    return x;
}
private static int allMethod2()
{
    int x = 0;
    for (int i = 0; i < 1000; i++)
        x = method2(i);
    for (int i = 1000; i < 100000; i += 10)
        x = method2(i);
    for (int i = 100000; i < 1000000; i += 100)
        x = method2(i);
    for (int i = 1000000; i < 2000000000; i += 200)
        x = method2(i);

    return x;
}
private static int allMethod3()
{
    int x = 0;
    for (int i = 0; i < 1000; i++)
        x = method3(i);
    for (int i = 1000; i < 100000; i += 10)
        x = method3(i);
    for (int i = 100000; i < 1000000; i += 100)
        x = method3(i);
    for (int i = 1000000; i < 2000000000; i += 200)
        x = method3(i);

    return x;
}
private static int allMethod4()
{
    int x = 0;
    for (int i = 0; i < 1000; i++)
        x = method4(i);
    for (int i = 1000; i < 100000; i += 10)
        x = method4(i);
    for (int i = 100000; i < 1000000; i += 100)
        x = method4(i);
    for (int i = 1000000; i < 2000000000; i += 200)
        x = method4(i);

    return x;
}

Опять же, тест:

  1. базовый метод (с String.length): 2145 мс
  2. метод log10: 711 мс = 3,02 раза быстрее, чем базовый уровень
  3. , повторное деление: 2797 мс = 0,77 раза быстрее, чем базовый уровень
  4. разделяй и властвуй: 74 мс = 28,99
    раз быстрее, чем базовый уровень

Изменить: После того, как я написал тест, я заглянул в Integer.toString из Java 6 и обнаружил, что он использует:

final static int [] sizeTable = { 9, 99, 999, 9999, 99999, 999999, 9999999,
                                  99999999, 999999999, Integer.MAX_VALUE };

// Requires positive x
static int stringSize(int x) {
    for (int i=0; ; i++)
        if (x <= sizeTable[i])
            return i+1;
}

Я сравнил его с моим решением «разделяй и властвуй»:

  1. разделяй и властвуй: 104 мс
  2. Решение Java 6 - повторить и сравнить: 406 мс

У меня примерно в 4 раза быстрее.

152
ответ дан 23 November 2019 в 00:06
поделиться
Другие вопросы по тегам:

Похожие вопросы: