В зависимости от вашего формата вы можете использовать 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
Ваше решение на основе String совершенно нормально, в нем нет ничего «неуместного». Вы должны понимать, что математически числа не имеют ни длины, ни цифр. Длина и цифры являются свойствами физического представления числа в определенной базе, то есть в строке.
Решение на основе логарифма делает (частично) те же вещи, что и решение на основе строк. внутренне, и, вероятно, делает это (незначительно) быстрее, потому что он производит только длину и игнорирует цифры. Но на самом деле я бы не стал считать это более ясным в намерениях - а это самый важный фактор.
Вы должны понимать, что математически числа не имеют ни длины, ни цифр. Длина и цифры являются свойствами физического представления числа в определенной базе, то есть в строке.Решение на основе логарифма делает (частично) те же вещи, что и решение на основе строк. внутренне, и, вероятно, делает это (незначительно) быстрее, потому что он производит только длину и игнорирует цифры. Но на самом деле я бы не стал считать это более ясным в намерениях - а это самый важный фактор.
Вы должны понимать, что математически числа не имеют ни длины, ни цифр. Длина и цифры являются свойствами физического представления числа в определенной базе, то есть в строке.Решение на основе логарифма делает (частично) те же вещи, что и решение на основе строк. внутренне, и, вероятно, делает это (незначительно) быстрее, потому что он производит только длину и игнорирует цифры. Но на самом деле я бы не стал считать это более ясным в намерениях - а это самый важный фактор.
и, вероятно, делает это (незначительно) быстрее, потому что он выдает только длину и игнорирует цифры. Но на самом деле я бы не стал считать это более ясным в намерениях - а это самый важный фактор. и, вероятно, делает это (незначительно) быстрее, потому что он выдает только длину и игнорирует цифры. Но на самом деле я бы не стал считать это более ясным в намерениях - а это самый важный фактор. Я записал эту функцию после взгляда 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;
}
}
}
Можно попробовать? ;)
на основе решения Дирка
final int digits = number==0?1:(1 + (int)Math.floor(Math.log10(Math.abs(number))));
Логарифм - ваш друг:
int n = 1000;
int length = (int)(Math.log10(n)+1);
NB: действительно только для n> 0.
Поскольку количество цифр в базе 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);
}
}
Отредактировано потому что в моем последнем редактировании был исправлен пример кода, но не описание.
Любопытно, я попытался протестировать его ...
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
Два комментария к вашему тесту: 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, чтобы уменьшить количество проходов цикла. Я не играл с этим.
Самый быстрый подход: разделяй и властвуй.
Предполагая, что ваш диапазон составляет от 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) - см. код ниже, чтобы увидеть, как был запущен тест:
Полный код:
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;
}
Опять же, тест:
Изменить: После того, как я написал тест, я заглянул в 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;
}
Я сравнил его с моим решением «разделяй и властвуй»:
У меня примерно в 4 раза быстрее.